Java多线程基础——Lock类

Java多线程基础——Lock类

之前已经说道,JVM提供了synchronized关键字来实现对变量的同步访问以及用wait和notify来实现线程间通信。在jdk1.5以后,JAVA提供了Lock类来实现和synchronized一样的功能,并且还提供了Condition来显示线程间通信。
Lock类是Java类来提供的功能,丰富的api使得Lock类的同步功能比synchronized的同步更强大。本文章的所有代码均在Lock类例子的代码
本文主要介绍一下内容:

  1. Lock类
  2. Lock类其他功能
  3. Condition类
  4. Condition类其他功能
  5. 读写锁

Lock类

Lock类实际上是一个接口,我们在实例化的时候实际上是实例化实现了该接口的类Lock lock = new ReentrantLock();。用synchronized的时候,synchronized可以修饰方法,或者对一段代码块进行同步处理。
前面讲过,针对需要同步处理的代码设置对象监视器,比整个方法用synchronized修饰要好。Lock类的用法也是这样,通过Lock对象lock,用lock.lock来加锁,用lock.unlock来释放锁。在两者中间放置需要同步处理的代码。
具体的例子如下:

public class MyConditionService {

    private Lock lock = new ReentrantLock();
    public void testMethod(){
        lock.lock();
        for (int i = 0 ;i < 5;i++){
            System.out.println("ThreadName = " + Thread.currentThread().getName() + (" " + (i + 1)));
        }
        lock.unlock();
    }
}

测试的代码如下:

        MyConditionService service = new MyConditionService();
        new Thread(service::testMethod).start();
        new Thread(service::testMethod).start();
        new Thread(service::testMethod).start();
        new Thread(service::testMethod).start();
        new Thread(service::testMethod).start();

        Thread.sleep(1000 * 5);

结果太长就不放出来,具体可以看我源码。总之,就是每个线程的打印1-5都是同步进行,顺序没有乱。
通过下面的例子,可以看出Lock对象加锁的时候也是一个对象锁,持续对象监视器的线程才能执行同步代码,其他线程只能等待该线程释放对象监视器。

public class MyConditionMoreService {

    private Lock lock = new ReentrantLock();
    public void methodA(){
        try{
            lock.lock();
            System.out.println("methodA begin ThreadName=" + Thread.currentThread().getName() +
                    " time=" + System.currentTimeMillis());
            Thread.sleep(1000 * 5);

            System.out.println("methodA end ThreadName=" + Thread.currentThread().getName() +
                    " time=" + System.currentTimeMillis());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void methodB(){
        try{
            lock.lock();
            System.out.println("methodB begin ThreadName=" + Thread.currentThread().getName() +
                    " time=" + System.currentTimeMillis());
            Thread.sleep(1000 * 5);

            System.out.println("methodB end ThreadName=" + Thread.currentThread().getName() +
                    " time=" + System.currentTimeMillis());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

测试代码如下:

 public void testMethod() throws Exception {
        MyConditionMoreService service = new MyConditionMoreService();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();

        ThreadA aa = new ThreadA(service);
        aa.setName("AA");
        aa.start();

        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();

        ThreadB bb = new ThreadB(service);
        bb.setName("BB");
        bb.start();

        Thread.sleep(1000 * 30);
    }
    
public class ThreadA extends Thread{

    private MyConditionMoreService service;

    public ThreadA(MyConditionMoreService service){
        this.service = service;
    }

    @Override
    public void run() {
        service.methodA();
    }
}

public class ThreadB extends Thread{

    private MyConditionMoreService service;

    public ThreadB(MyConditionMoreService service){
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.methodB();
    }
}

结果如下:

methodA begin ThreadName=A time=1485590913520
methodA end ThreadName=A time=1485590918522
methodA begin ThreadName=AA time=1485590918522
methodA end ThreadName=AA time=1485590923525
methodB begin ThreadName=B time=1485590923525
methodB end ThreadName=B time=1485590928528
methodB begin ThreadName=BB time=1485590928529
methodB end ThreadName=BB time=1485590933533

可以看出Lock类加锁确实是对象锁。针对同一个lock对象执行的lock.lock是获得对象监视器的线程才能执行同步代码 其他线程都要等待。
在这个例子中,加锁,和释放锁都是在try-finally。这样的好处是在任何异常发生的情况下,都能保障锁的释放。

Lock类其他的功能

如果Lock类只有lock和unlock方法也太简单了,Lock类提供了丰富的加锁的方法和对加锁的情况判断。主要有

  • 实现锁的公平
  • 获取当前线程调用lock的次数,也就是获取当前线程锁定的个数
  • 获取等待锁的线程数
  • 查询指定的线程是否等待获取此锁定
  • 查询是否有线程等待获取此锁定
  • 查询当前线程是否持有锁定
  • 判断一个锁是不是被线程持有
  • 加锁时如果中断则不加锁,进入异常处理
  • 尝试加锁,如果该锁未被其他线程持有的情况下成功

实现公平锁

在实例化锁对象的时候,构造方法有2个,一个是无参构造方法,一个是传入一个boolean变量的构造方法。当传入值为true的时候,该锁为公平锁。默认不传参数是非公平锁。

公平锁:按照线程加锁的顺序来获取锁
非公平锁:随机竞争来得到锁
此外,JAVA还提供isFair()来判断一个锁是不是公平锁。

获取当前线程锁定的个数

Java提供了getHoldCount()方法来获取当前线程的锁定个数。所谓锁定个数就是当前线程调用lock方法的次数。一般一个方法只会调用一个lock方法,但是有可能在同步代码中还有调用了别的方法,那个方法内部有同步代码。这样,getHoldCount()返回值就是大于1。

下面的方法用来判断等待锁的情况

获取等待锁的线程数

Java提供了getQueueLength()方法来得到等待锁释放的线程的个数。

查询指定的线程是否等待获取此锁定

Java提供了hasQueuedThread(Thread thread)查询该Thread是否等待该lock对象的释放。

查询是否有线程等待获取此锁定

同样,Java提供了一个简单判断是否有线程在等待锁释放即hasQueuedThreads()

下面的方法用来判断持有锁的情况

查询当前线程是否持有锁定

Java不仅提供了判断是否有线程在等待锁释放的方法,还提供了是否当前线程持有锁即isHeldByCurrentThread(),判断当前线程是否有此锁定。

判断一个锁是不是被线程持有

同样,Java提供了简单判断一个锁是不是被一个线程持有,即isLocked()

下面的方法用来实现多种方式加锁

加锁时如果中断则不加锁,进入异常处理

Lock类提供了多种选择的加锁方法,lockInterruptibly()也可以实现加锁,但是当线程被中断的时候,就会加锁失败,进行异常处理阶段。一般这种情况出现在该线程已经被打上interrupted的标记了。

尝试加锁,如果该锁未被其他线程持有的情况下成功

Java提供了tryLock()方法来进行尝试加锁,只有该锁未被其他线程持有的基础上,才会成功加锁。

上面介绍了Lock类来实现代码的同步处理,下面介绍Condition类来实现wait/notify机制。

Condition类

Condition是Java提供了来实现等待/通知的类,Condition类还提供比wait/notify更丰富的功能,Condition对象是由lock对象所创建的。但是同一个锁可以创建多个Condition的对象,即创建多个对象监视器。这样的好处就是可以指定唤醒线程。notify唤醒的线程是随机唤醒一个。
下面,看一个例子,显示简单的等待/通知

public class ConditionWaitNotifyService {

    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();


    public void await(){
        try{
            lock.lock();
            System.out.println("await的时间为 " + System.currentTimeMillis());
            condition.await();
            System.out.println("await结束的时间" + System.currentTimeMillis());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }


    public void signal(){
        try{
            lock.lock();
            System.out.println("sign的时间为" + System.currentTimeMillis());
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}

测试的代码如下:

        ConditionWaitNotifyService service = new ConditionWaitNotifyService();
        new Thread(service::await).start();
        Thread.sleep(1000 * 3);
        service.signal();
        Thread.sleep(1000);

结果如下:

await的时间为 1485610107421
sign的时间为1485610110423
await结束的时间1485610110423

condition对象通过lock.newCondition()来创建,用condition.await()来实现让线程等待,是线程进入阻塞。用condition.signal()来实现唤醒线程。唤醒的线程是用同一个conditon对象调用await()方法而进入阻塞。并且和wait/notify一样,await()和signal()也是在同步代码区内执行。
此外看出await结束的语句是在获取通知之后才执行,确实实现了wait/notify的功能。下面这个例子是展示唤醒制定的线程。

        ConditionAllService service = new ConditionAllService();
        Thread a = new Thread(service::awaitA);
        a.setName("A");
        a.start();

        Thread b = new Thread(service::awaitB);
        b.setName("B");
        b.start();

        Thread.sleep(1000 * 3);

        service.signAAll();

        Thread.sleep(1000 * 4);

结果如下:

begin awaitA时间为 1485611065974ThreadName=A
begin awaitB时间为 1485611065975ThreadName=B
signAll的时间为1485611068979ThreadName=main
end awaitA时间为1485611068979ThreadName=A

该结果确实展示用同一个condition对象来实现等待通知。
对于等待/通知机制,简化而言,就是等待一个条件,当条件不满足时,就进入等待,等条件满足时,就通知等待的线程开始执行。为了实现这种功能,需要进行wait的代码部分与需要进行通知的代码部分必须放在同一个对象监视器里面。执行才能实现多个阻塞的线程同步执行代码,等待与通知的线程也是同步进行。对于wait/notify而言,对象监视器与等待条件结合在一起 即synchronized(对象)利用该对象去调用wait以及notify。但是对于Condition类,是对象监视器与条件分开,Lock类来实现对象监视器,condition对象来负责条件,去调用await以及signal。

Condition类的其他功能

和wait类提供了一个*长等待时间,awaitUntil(Date deadline)在到达指定时间之后,线程会自动唤醒。但是无论是await或者awaitUntil,当线程中断时,进行阻塞的线程会产生中断异常。Java提供了一个awaitUninterruptibly的方法,使即使线程中断时,进行阻塞的线程也不会产生中断异常。

读写锁

Lock类除了提供了ReentrantLock的锁以外,还提供了ReentrantReadWriteLock的锁。读写锁分成两个锁,一个锁是读锁,一个锁是写锁。读锁与读锁之间是共享的,读锁与写锁之间是互斥的,写锁与写锁之间也是互斥的。
看下面的读读共享的例子:

public class ReadReadService {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read(){
        try{
            try{
                lock.readLock().lock();
                System.out.println("获得读锁" + Thread.currentThread().getName() +
                " " + System.currentTimeMillis());
                Thread.sleep(1000 * 10);
            }finally {
                lock.readLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

测试的代码和结果如下:

        ReadReadService service = new ReadReadService();
        Thread a = new Thread(service::read);
        a.setName("A");

        Thread b = new Thread(service::read);
        b.setName("B");

        a.start();
        b.start();

结果如下:

获得读锁A 1485614976979
获得读锁B 1485614976981

两个线程几乎同时执行同步代码。
下面的例子是写写互斥的例子

public class WriteWriteService {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void write(){
        try{
            try{
                lock.writeLock().lock();
                System.out.println("获得写锁" + Thread.currentThread().getName() +
                        " " +System.currentTimeMillis());
                Thread.sleep(1000 * 10);
            }finally {
                lock.writeLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

测试代码和结果如下:

        WriteWriteService service = new WriteWriteService();
        Thread a = new Thread(service::write);
        a.setName("A");
        Thread b = new Thread(service::write);
        b.setName("B");

        a.start();
        b.start();
        Thread.sleep(1000 * 30);

结果如下:

获得写锁A 1485615316519
获得写锁B 1485615326524

两个线程同步执行代码

读写互斥的例子:

public class WriteReadService {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){
        try{
            try{
                lock.readLock().lock();
                System.out.println("获得读锁" + Thread.currentThread().getName()
                        + " " + System.currentTimeMillis());
                Thread.sleep(1000 * 10);
            }finally {
                lock.readLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public void write(){
        try{
            try{
                lock.writeLock().lock();
                System.out.println("获得写锁" + Thread.currentThread().getName()
                        + " " + System.currentTimeMillis());
                Thread.sleep(1000 * 10);
            }finally {
                lock.writeLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

}

测试的代码如下:

        WriteReadService service = new WriteReadService();
        Thread a = new Thread(service::write);
        a.setName("A");
        a.start();
        Thread.sleep(1000);

        Thread b = new Thread(service::read);
        b.setName("B");
        b.start();

        Thread.sleep(1000 * 30);

结果如下:

获得写锁A 1485615633790
获得读锁B 1485615643792

两个线程读写之间也是同步执行代码。

本文介绍了新的同步代码的方式Lock类以及新的等待/通知机制的实现Condition类。

Java并发包中Lock的实现原理

Java并发包中Lock的实现原理

1. Lock 的简介及使用

Lock是java 1.5中引入的线程同步工具,它主要用于多线程下共享资源的控制。本质上Lock仅仅是一个接口(位于源码包中的java\util\concurrent\locks中),它包含以下方法

//尝试获取锁,获取成功则返回,否则阻塞当前线程
void lock(); 

//尝试获取锁,线程在成功获取锁之前被中断,则放弃获取锁,抛出异常 
void lockInterruptibly() throws InterruptedException; 

//尝试获取锁,获取锁成功则返回true,否则返回false 
boolean tryLock(); 

//尝试获取锁,若在规定时间内获取到锁,则返回true,否则返回false,未获取锁之前被中断,则抛出异常 
boolean tryLock(long time, TimeUnit unit)
                                   throws InterruptedException; 

//释放锁
void unlock(); 

//返回当前锁的条件变量,通过条件变量可以实现类似notify和wait的功能,一个锁可以有多个条件变量
Condition newCondition();

Lock有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock。

使用方法:多线程下访问(互斥)共享资源时, 访问前加锁,访问结束以后解锁,解锁的操作推荐放入finally块中。

Lock l = ...; //根据不同的实现Lock接口类的构造函数得到一个锁对象 
l.lock(); //获取锁位于try块的外面 
try { 
      // access the resource protected by this lock 
} finally { 
     l.unlock(); 
}

注意,加锁位于对资源访问的try块的外部,特别是使用lockInterruptibly方法加锁时就必须要这样做,这为了防止线程在获取锁时被中断,这时就不必(也不能)释放锁。

try {
     l.lockInterruptibly();//获取锁失败时不会执行finally块中的unlock语句
      try{
          // access the resource protected by this lock
     }finally{
          l.unlock();
     }
} catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
}

2. 实现Lock接口的基本思想

需要实现锁的功能,两个必备元素,一个是表示(锁)状态的变量(我们假设0表示没有线程获取锁,1表示已有线程占有锁),另一个是队列,队列中的节点表示因未能获取锁而阻塞的线程。为了解决多核处理器下多线程缓存不一致的问题,表示状态的变量必须声明为voaltile类型,并且对表示状态的变量和队列的某些操作要保证原子性和可见性。原子性和可见性的操作主要通过Atomic包中的方法实现。

      线程获取锁的大致过程(这里没有考虑可重入和获取锁过程被中断或超时的情况)

1. 读取表示锁状态的变量

2. 如果表示状态的变量的值为0,那么当前线程尝试将变量值设置为1(通过CAS操作完成),当多个线程同时将表示状态的变量值由0设置成1时,仅一个线程能成功,其

它线程都会失败

2.1 若成功,表示获取了锁,

2.1.1 如果该线程(或者说节点)已位于在队列中,则将其出列(并将下一个节点则变成了队列的头节点)

2.1.2 如果该线程未入列,则不用对队列进行维护

然后当前线程从lock方法中返回,对共享资源进行访问。

2.2 若失败,则当前线程将自身放入等待(锁的)队列中并阻塞自身,此时线程一直被阻塞在lock方法中,没有从该方法中返回(被唤醒后仍然在lock方法中,并从下一条语句继续执行,这里又会回到第1步重新开始)

3. 如果表示状态的变量的值为1,那么将当前线程放入等待队列中,然后将自身阻塞(被唤醒后仍然在lock方法中,并从下一条语句继续执行,这里又会回到第1步重新开始)

          注意: 唤醒并不表示线程能立刻运行,而是表示线程处于就绪状态,仅仅是可以运行而已

 

      线程释放锁的大致过程

1. 释放锁的线程将状态变量的值从1设置为0,并唤醒等待(锁)队列中的队首节点,释放锁的线程从就从unlock方法中返回,继续执行线程后面的代码

2. 被唤醒的线程(队列中的队首节点)和可能和未进入队列并且准备获取的线程竞争获取锁,重复获取锁的过程

注意:可能有多个线程同时竞争去获取锁,但是一次只能有一个线程去释放锁,队列中的节点都需要它的前一个节点将其唤醒,例如有队列A<-B-<C ,即由A释放锁时唤醒B,B释放锁时唤醒C

 

3. 公平锁和非公平锁

锁可以分为公平锁和不公平锁,重入锁和非重入锁(关于重入锁的介绍会在ReentrantLock源代码分析中介绍),以上过程实际上是非公平锁的获取和释放过程。

公平锁严格按照先来后到的顺去获取锁,而非公平锁允许插队获取锁。

公平锁获取锁的过程上有些不同,在使用公平锁时,某线程想要获取锁,不仅需要判断当前表示状态的变量的值是否为0,还要判断队列里是否还有其他线程,若队列中还有线程则说明当前线程需要排队,进行入列操作,并将自身阻塞;若队列为空,才能尝试去获取锁。而对于非公平锁,当表示状态的变量的值是为0,就可以尝试获取锁,不必理会队列是否为空,这样就实现了插队获取锁的特点。通常来说非公平锁的吞吐率比公平锁要高,我们一般常用非公平锁。

这里需要解释一点,什么情况下才会出现,表示锁的状态的变量的值是为0而且队列中仍有其它线程等待获取锁的情况。

假设有三个线程A、B、C。A线程为正在运行的线程并持有锁,队列中有一个C线程,位于队首。现在A线程要释放锁,具体执行的过程操作可分为两步:

1. 将表示锁状态的变量值由1变为0,

2. C线程被唤醒,这里要明确两点:

              (1)C线程被唤醒并不代表C线程开始执行,C线程此时是处于就绪状态,要等待操作系统的调度

              (2)C线程目前还并未出列,C线程要进入运行状态,并且通过竞争获取到锁以后才会出列。

如果C线程此时还没有进入运行态,同时未在队列中的B线程进行获取锁的操作,B就会发现虽然当前没有线程持有锁,但是队列不为空(C线程仍然位于队列中),要满足先来后到的特点(B在C之后执行获取锁的操作),B线程就不能去尝试获取锁,而是进行入列操作。

 

4. 实现Condition接口的基本思想

Condition 本质是一个接口,它包含如下方法

// 让线程进入等通知待状态
void await() throws InterruptedException; 
void awaitUninterruptibly();

//让线程进入等待通知状态,超时结束等待状态,并抛出异常  
long awaitNanos(long nanosTimeout) throws InterruptedException; 
boolean await(long time, TimeUnit unit) throws InterruptedException; 
boolean awaitUntil(Date deadline) throws InterruptedException; 

//将条件队列中的一个线程,从等待通知状态转换为等待锁状态 
void signal(); 

//将条件队列中的所有线程,从等待通知阻塞状态转换为等待锁阻塞状态
void signalAll();

一个Condition实例的内部实际上维护了一个队列,队列中的节点表示由于(某些条件不满足而)线程自身调用await方法阻塞的线程。Condition接口中有两个重要的方法,即 await方法和 signal方法。线程调用这个方法之前该线程必须已经获取了Condition实例所依附的锁。这样的原因有两个,(1)对于await方法,它内部会执行释放锁的操作,所以使用前必须获取锁。(2)对于signal方法,是为了避免多个线程同时调用同一个Condition实例的singal方法时引起的(队列)出列竞争。下面是这两个方法的执行流程。

await方法:

1. 入列到条件队列(注意这里不是等待锁的队列

2. 释放锁

3. 阻塞自身线程

————被唤醒后执行————-

4. 尝试去获取锁(执行到这里时线程已不在条件队列中,而是位于等待(锁的)队列中,参见signal方法)

4.1 成功,从await方法中返回,执行线程后面的代码

4.2 失败,阻塞自己(等待前一个节点释放锁时将它唤醒)

注意:await方法时自身线程调用的,线程在await方法中阻塞,并没有从await方法中返回,当唤醒后继续执行await方法中后面的代码(也就是获取锁的代码)。可以看出await方法释放了锁,又尝试获得锁。当获取锁不成功的时候当前线程仍然会阻塞到await方法中,等待前一个节点释放锁后再将其唤醒。

 

signal方法:

1. 将条件队列的队首节点取出,放入等待锁队列的队尾

2. 唤醒该节点对应的线程

注意:signal是由其它线程调用

condition

Lock和Condition的使用例程

下面这个例子,就是利用lock和condition实现B线程先打印一句信息后,然后A线程打印两句信息(不能中断),交替十次后结束

public class ConditionDemo {
    volatile int key = 0;
    Lock l = new ReentrantLock();
    Condition c = l.newCondition();
    
    public static  void main(String[] args){
        ConditionDemo demo = new ConditionDemo();
        new Thread(demo.new A()).start();
        new Thread(demo.new B()).start();
    }
    
    class A implements Runnable{
        @Override
        public void run() {
            int i = 10;
            while(i > 0){
                l.lock();
                try{
                    if(key == 1){
                        System.out.println("A is Running");
                        System.out.println("A is Running");
                        i--;
                        key = 0;
                        c.signal();
                    }else{
                     c.awaitUninterruptibly();                        
                    }
                    
                }
                finally{
                    l.unlock();
                }
            }
        }
        
    }
    
    class B implements Runnable{
        @Override
        public void run() {
            int i = 10;
            while(i > 0){
                l.lock();
                try{
                    if(key == 0){
                        System.out.println("B is Running");
                        i--;
                        key = 1;
                        c.signal();
                    }else{
                     c.awaitUninterruptibly();                        
                    }
                    
                }
                finally{
                    l.unlock();
                }
            }
        }    
    }
}

5. Lock与synchronized的区别

1. Lock的加锁和解锁都是由java代码配合native方法(调用操作系统的相关方法)实现的,而synchronize的加锁和解锁的过程是由JVM管理的

2. 当一个线程使用synchronize获取锁时,若锁被其他线程占用着,那么当前只能被阻塞,直到成功获取锁。而Lock则提供超时锁和可中断等更加灵活的方式,在未能获取锁的     条件下提供一种退出的机制。

3. 一个锁内部可以有多个Condition实例,即有多路条件队列,而synchronize只有一路条件队列;同样Condition也提供灵活的阻塞方式,在未获得通知之前可以通过中断线程以    及设置等待时限等方式退出条件队列。

4. synchronize对线程的同步仅提供独占模式,而Lock即可以提供独占模式,也可以提供共享模式

Android进阶笔记:AIDL内部实现详解 (二)

Android进阶笔记:AIDL内部实现详解 (二)

 

Android学习笔记

订阅专栏
接着上一篇分析的aidl的流程解析。知道了aidl主要就是利用Ibinder来实现跨进程通信的。既然是通过对Binder各种方法的封装,那也可以不使用aidl自己通过Binder来实现跨进程通讯。那么这篇博客就主要就写一下通过上篇(Android进阶笔记:AIDL详解(一))总结的知识来自己实现跨进程通讯从而更加透彻的了解aidl的核心逻辑。

首先上一篇(Android进阶笔记:AIDL详解(一))中总结出一个结论————“onTransact方法是提供给server端用的,transact方法(内部类proxy封装了transact方法)和asInterface方法是给client端用的。”因此很清楚,只要我们在Server端实现跨进程需要调用的方法(类似aidl的接口实现)和onTransact方法,而服务端只要通过获得的IBinder对象来调用transact方法就可以代替aidl来实现跨进程通讯了。既然思路已经整理清楚了,那就一步一步来实现它。

Server端
首先Server端是要通过Service的onBind方法来给Client端一个Binder对象,那就先从这个Binder对象入手。那就先来创建了一个MyBinder类,代码如下:

MyBinder.java

public class MyBinder extends Binder {
//标记方法的
private static final int METHOD_ADD_CODE = 1001;
//标识binder对象的
private static final String DESCRIPTION = “not use aidl”;

@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (code == METHOD_ADD_CODE) {
//验证一下binder
data.enforceInterface(DESCRIPTION);
//从parcel对象中读取参数
int arg0 = data.readInt();
int arg1 = data.readInt();
//写入结果
reply.writeInt(add(arg0, arg1));
return true;
}
return super.onTransact(code, data, reply, flags);
}

private int add(int arg0, int arg1) {
return arg0 + arg1;
}
}

代码非常简单,只是重新写了一下onTransact方法。其实一共只有4步:

根据code的值来判断client端具体想要调用哪个方法;
读取parcel对象(data)中传入的参数;
调用自己本地的方法(add)并将参数传入;
把结果写入parcel对象(reply)中;
接着只要把这个自己定义的MyBinder类的实例通过Service.onBInder方法返回给Client端就可以了。

MyService.java

public class MyService extends Service {
private MyBinder myBinder;

public MyService() {
}

@Override
public void onCreate() {
super.onCreate();
//创建实例
myBinder = new MyBinder();
}

@Override
public IBinder onBind(Intent intent) {
//返回自定义的binder对象
return myBinder;
}
}

Client端
client端的代码无非就是把之前写在aidl中的proxy内部类的方法拿出来了。具体看代码:

WithoutAidlActivity.java

public class WithoutAidlActivity extends AppCompatActivity {

private ServiceConnection serviceConnection;
private IBinder binder;

//以下两个参数要和server端保持一致
//标记方法的(告知server端调用哪个方法)
private static final int METHOD_ADD_CODE = 1001;
//标识binder对象的
private static final String DESCRIPTION = “not use aidl”;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_without_aidl);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(“onServiceConnected”, “onServiceConnected: connected success!”);
binder = service;
//这里就代替aidl中的proxy来直接调用transact方法
//先准备参数
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(DESCRIPTION);
data.writeInt(123);
data.writeInt(456);
try {
//调用transact方法
binder.transact(METHOD_ADD_CODE, data, reply, 0);
//获得结果
int result = reply.readInt();
Log.d(“onServiceConnected”, “result = ” + result);
} catch (RemoteException e) {
e.printStackTrace();
} finally {
data.recycle();
reply.recycle();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
binder = null;
}
};

bindService(new Intent(“com.coder_f.aidlserver.MyService”), serviceConnection, BIND_AUTO_CREATE);

}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}

首先连接成功后在serviceConnection.onServiceConnected方法中获得了IBinder实例,然后总共做了3个事情:

创建两个parcel对象分别存放参数(data)和返回值(reply)
调用transact方法,传入data,reply,和你要调用的方法code。*后的flag传入0表示有返回值(1表示没有又返回值)
从reply中获得结果
完成以上工作就可以不通过aidl实现跨进程通讯了。但是还是要说一下,这里我们server端调用的只是一个简单的add方法不耗时的,而transact方法则是在onServiceConnected方法中被调用的其实是在主线程中执行的。如果add方法换成一个耗时方法,那么主线程(UI线程)是会卡死的,调用transact方法时当前线程会被挂起知道结果被返回(有兴趣可以去试试,只要在add方法里面加一个Thread.sleep就可以了)。所以*好的办法就是起一个线程来调用transact方法。

Android进阶笔记:AIDL内部实现详解 (一)

AIDL内部实现详解 (一)
AIDL的作用是实现跨进程通讯使用方法也非常的简单,他的设计模式是典型的C/S架构。使用AIDL只要在Client端和Server端的项目根目录下面创建一个aidl的文件夹,在aidl文件夹的下面用java代码编写一个后缀名为.aidl的接口文件然后重新编译一下就会在gen目录下生成相对应的java文件。这里主要研究aidl的运作流程以及原理。

aidl结构
首先我在Server端去实现了一个aidl的接口文件代码如下:

//IMyAidl.aidl
interface IMyAidl {
int add(int arg1, int aarg2);
}

重新编译(rebuild)就会在generated目录下面生成相应的IMyAidl.java的文件代码如下:

public interface IMyAidl extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.coder_f.aidlserver.IMyAidl {
private static final java.lang.String DESCRIPTOR = “com.coder_f.aidlserver.IMyAidl”;

/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.coder_f.aidlserver.IMyAidl interface,
* generating a proxy if needed.
*/
public static com.coder_f.aidlserver.IMyAidl asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.coder_f.aidlserver.IMyAidl))) {
return ((com.coder_f.aidlserver.IMyAidl) iin);
}
return new com.coder_f.aidlserver.IMyAidl.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.coder_f.aidlserver.IMyAidl {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public int add(int arg1, int arg2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(arg1);
_data.writeInt(arg2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public int add(int arg1, int arg2) throws android.os.RemoteException;
}

主要就是根据aidl文件自动生成了stub的内部类,这个内部类不仅继承了Binder还继承了父类。这里继承父类主要是为了能够获得回调add接口方法,stub类并没有去真正的实现add方法。

stub类结构很清楚里面只有3个方法加上1个内部类分别是:

asInterface 根据传入的binder来判断当前是否是跨进程,如果跨进程则返回一个Proxy对象,如果不是跨进程则直接返回当前接口实现类(想当是调用本地接口);
asBinder 返回当前binder对象,继承IInterface必须实现的方法(满足内部跨进程)
onTransact Binder的方法当client端的Binder对象调用了transact方法时此方法会被回调
Proxy类 一个代理类,主要作用是配置Binder对象的transact方法(这个方法继承自IBinder)需要的参数以及调用transact方法。Proxy类也继承了我们自己定义的aidl接口,主要为了保证接口中的方法都有相对应transact去调用真正的server端方法(应为继承了接口,如果不是抽象类那就必须得去实现这些方法吧)。
这里还有一个特别的参数就是TRANSACTION_add这么一个常量,他是由一个常量+0构成的,这个常量其实就是0,这个常量主要来标记方法的如果有多个方法:

//当前只有一个方法所以是+0,当更多的方法时每个方法都会对应一个数字。例如有两个时那就会如下
static final int TRANSACTION_xxx = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_yyy = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);

这些方法(类)现在有一个印象就可以,后面会详细的分析。

以上是一个aidl的完整结构,当然真正要使用还需要去有一个类来继承stub类并且实现*初aidl文件中定义的所有接口(注意stub类是一个抽象类,它继承了aidl的接口但却没有真正的去实现它,这里需要我们来实现)。当然这个java类也需要在client端实现一遍(也就是自动生成一遍)。

aidl流程
要分析aidl流程所以我先创建了一个Service(MyService),在里面创建了一个内部类MyAidlImpl来继承IMyAidl.Stub并且实现我们*初定义在aidl文件中一直未被实现的add方法。在Service的onBind方法中返回这个内部类的实例(因为Stub是继承了Binder类所以MyAidlImpl的实例也会是Binder类型),具体代码如下:

Server端:MyService.java

//MyService.java
public class MyService extends Service {
private MyAidlImpl myAidlImpl;

public MyService() {
}

@Override
public void onCreate() {
super.onCreate();
myAidlImpl = new MyAidlImpl();
}

@Override
public IBinder onBind(Intent intent) {

return myAidlImpl;
}
}

class MyAidlImpl extends IMyAidl.Stub {
@Override
public int add(int arg1, int arg2) throws RemoteException {

return arg1 + arg2;
}
}

接口的实现方法很简单,就是把传进来的两个参数加一下再返回回去。现在这个Service就相当于是一个Server端,来远程处理一些任务(这里就是arg1 + arg2)并且返回结果。

至于Client端代码就是*普通的一个Activity里面去绑定刚刚实现的Service,这里我为了保证是跨进程我重新创建了一个项目。(也可以在当前项目里面来做,只要在manifest文件中把Service的android:process设置为“:remote”就可以了,这样Service就不会和activity运行在一个进程中)

Client端:MainActivity.java

public class MainActivity extends AppCompatActivity {
private static final String TAG = “AIDLClient”;
private ServiceConnection serviceConnection;
private IMyAidl myAidl;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myAidl = IMyAidl.Stub.asInterface(service);
try {
Log.d(TAG, “onServiceConnected: data from server = ” + myAidl.add(123, 456));
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
myAidl = null;
}
};

bindService(new Intent(“com.coder_f.aidlserver.MyService”), serviceConnection, BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}

根据上面代码我们现在看一下,首先是Client端来bindService,这里绑定成功以后就会返回一个Binder对象,这个对象就是Server端onBind的时候返回的,是MyAidlImpl的实例,通过IMyAidl.Stub.asInterface(service)方法来获得一个Proxy类的对象(这里考虑的是跨进程调用的情况所以返回的是proxy对象),然后我们调用了proxy对象里面的add方法,那我们就跟着去看一下根据aidl自动生成的java类中proxy的add方法里面具体是什么逻辑。

private static class Proxy implements com.coder_f.aidlserver.IMyAidl {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public int add(int arg1, int arg2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(arg1);
_data.writeInt(arg2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

这里的remote就是asInterfa方法中传入的Binder对象,通过这个Binder对象,接着调用了add方法。这里来仔细看看这个add方法的逻辑,总共就三步。

创建了两个Parcel对象(跨进程只能传递Parcel以及Binder类型的对象)_data用来存放参数,_reply用来存放返回值。这里传入的DESCRIPTOR是一个包名。这个DESCRIPTOR也起到标识的作用,只有transact和onTransact方法中parcel对象的一致时才能成功调用。这也是为什么我们要求Server端和Client端aidl存放目录结构一致的原因,不一致的话这个自动生成DESCRIPTOR也会不一样;

调用了Binder的transact方法,这个方法其实就是跨进程调用的核心,他的*个参数是一个int值,用来告诉Server端(onTransact方法)我要调用的具体是哪个方法,这里Stub.TRANSACTION_add标志的就是add方法,第二个和第三个之前已经说了分别是参数和返回值,第四个是个标志位,当0的时候代表有返回值,当1的时候则代表没有返回值(此时就算你的方法真的有返回值_reply中也不会有结果)

读取_reply中返回的结果。

这里要注意的是当调用transact方法时当前线程会被挂起。此时如果这个方法运行在主线程时,而server端又因为各种原因或者方法本身就是耗时的导致不能及时返回结果就会使得ui线程卡住。而我上面Client端方法中myAidl.add(123,456)是放在了onServiceConnected方法里面,而onServiceConnected方法其实是运行在主线程的(具体可以看我之前bindservice流程分析文章中*后面的说明)

这里调用Binder的transact方法以后经过一系列的流程(这里具体什么流程没有仔细研究过,涉及到jni底层native的binder方法)*后会回调到Server端的onTransact方法。

好了那我们就看看自动给我们生成的java文件中onTransact方法的逻辑代码如下。

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

这里的onTransact方法的参数其实和transact方法的参数是一一对应的,*个就是标志方法的code默认会有有一个INTERFACE_TRANSACTION用来验证之前说的DESCRIPTOR,另外一个就是TRANSACTION_add,这里我们接口中只有一个所以总共onTransact方法中只对这一个方法处理。

这个add方法和proxy类中add方法的结构很类似,就是从_data中取出(proxy.add是创建写入)参数调用真正add方法(在service中实现的add方法)然后把结果写入到reply(proxy.add是创建读取)中。注意这个方法有一个返回值,如果返回false则Client端会调用失败。

上面就是aidl的一整套流程。

总结:
aidl其实就是利用Binder来实现跨进程调用。而这个系统根据.aidl文件生成的java文件就是对binder的transact方法进行封装。

onTransact方法是提供给server端用的,transact方法(内部类proxy封装了transact方法)和asInterface方法是给client端用的。系统自动生成不知道你要把哪个当做server端哪个当做client端所以就都生成了。

DESCRIPTION是根据aidl文件位置(包名)来生成的,DESCRIPTION是binder的唯一标识,这也解释了为什么要保证client端和server端.adil文件的包结构一样;

aidl的transact的调用会导致当前线程挂起,因此如果Server端的方法耗时*好另起线程来调用transact方法;

aidl通过Parcel来传递参数和返回值,因为binder只支持Binder对象和parcel对象的跨进程调用;

Binder,IBinder,IInterface三个的关系。Binder是继承自IBinder对象的,IBinder是远程对象的基本接口。另外IBinder的主要API是transact(),与它对应另一方法是Binder.onTransact(),而IInterface主要为aidl服务,里面就一个方法——asBinder,用来获得当前binder对象(方便系统内部调用)。

*后说白了aidl的核心就是client调用binder的transact方法,server通过binder的onTransact方法了来执行相应的方法并返回。

Android进阶笔记:Messenger源码详解

Android进阶笔记:Messenger源码详解

Messenger可以理解为一个是用于发送消息的一个类用法也很多,这里主要分析一下再跨进程的情况下Messenger的实现流程与源码分析。相信结合前面两篇关于aidl解析文章能够更好的对aidl有一个认识。(Android进阶笔记:AIDL内部实现详解 (一)、Android进阶笔记:AIDL内部实现详解 (二))

用法说明

先来看一下Messenger在跨进程通讯时的使用方法,代码如下:

Service的代码

//用来传递Messenger中IMessenger
public class ServerService extends Service {
    public static final String TAG = "ServerService";
    private Messenger messenger;

    @Override
    public void onCreate() {
        super.onCreate();
        //创建一个Messenger对象
        messenger = new Messenger(new MessengerHandler());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回IMessenger
        return messenger.getBinder();
    }
}
//用于创建Messenger的Handler
class MessengerHandler extends Handler {
    public static final String TAG = "ServerService";

    @Override
    public void handleMessage(Message msg) {
        if (msg.what == 10001) {
            String data = msg.getData().getString("data");
            data = data == null ? "null" : data;
            Log.e(TAG, "handleMessage: get msg from client = (" + data + ")");
        }
    }
}

上面就是Service的代码,分析一下其实总共做了3步:

  1. 定义了一个MessengerHandler的内部类并且实现了handleMessage的内部回调;
  2. 创建了一个Messenger的对象,在Messenger的构造函数中传入刚刚创建的handler实例;
  3. 在Service的onBind方法中回调messenger的getBinder()方法;

Activity的代码

public class MainActivity extends AppCompatActivity {
    private ServiceConnection serviceConnection;
    private Messenger messenger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("MainActivity", "onServiceConnected: connection success !!!");
                //用返回的Ibinder对象来构造一个Messenger实例
                messenger = new Messenger(service);

                //创建一个msg
                Message msg = new Message();
                msg.what = 10001;
                Bundle bundle = new Bundle();
                bundle.putString("data", "hello Server");
                msg.setData(bundle);
                try {
                //调用messenger的send方法
                    messenger.send(msg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                messenger = null;
            }
        };
        bindService(new Intent("com.coder_f.messengerdemo.ServerService"), serviceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

Activity中做的工作其实也不复杂也是3步:

  1. 通过绑定Service来获得IBinder对象;
  2. 通过IBinder对象重新构造一个Messenger;
  3. 通过Messenger的send方法来发送消息;

以上就是Messenger的使用方法。好了现在就可以根据上面的使用方法来看看Messenger内部到底是怎么来运作的。

源码解析

首先先来看看参数为Handler的Messenger的构造函数

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

很简单,就是保存传入Handler的getIMessenger()方法返回的东西。那接下来就去看看getIMessenger()方法返回的是什么东西。

final IMessenger getIMessenger() {
   synchronized (mQueue) {
        if (mMessenger != null) {
           return mMessenger;
        }
        mMessenger = new MessengerImpl();
        return mMessenger;
    }
}

也不复杂,无非就是判断一下mMessenger是不是空然后返回一下。那mMessenger到底是什么呢。看看它的构造函数new MessengerImpl()代码如下:

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

看到这里相信已经摸的差不多了,这个结构就是aidl的结构。这里猜一下应该就明白了,源码里面肯定有定义了一个IMessage.aidl,而且里面还声明了一个send的方法。而这个seng的方法在Handler里面被实现了,具体就是通过Handler来发送一条消息。那么可以得到结论,*后mTarget获得的其实就是一个IMessenger.Stub的实例,里面已经实现了接口中的方法(send(Message msg))

事实证明确实如此,源码的位置:platform\frameworks\base\core\java\android\os\IMessenger.aidl

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

上面只是对构造函数的源码进行了分析,但是其实已经把Messenger的结构摸的八九不离十了;

好了既然构造函数分析的差不多了,根据流程下一步应该是通过messenger.getBinder()方法取出一个Ibinder对象通过Service来返回给Activity。那么接下来再来看看messenger.getBinder()方法:

    public IBinder getBinder() {
        return mTarget.asBinder();
    }

很清楚,调用了IInterface(mTarget就是IMessenger.Stub的实例继承了IMessenger,而IMessenger继承了IInterface)的asBInder方法返回了一个Binder(这里简单的理解其实就是返回了它自己,因为stub内部类也继承了Binder)。

service这边的代码只有这些,那根据上面的使用方法,继续来看Activity这边的代码吧。
Activity这边也有一个构造函数,参数是一个IBinder对象,这个构造函数的源码如下:

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

很清楚返回的其实就是一个proxy类也就是一个binder驱动(不明白的可以看之前的博客)。
然后接下来就是调用了Messenger的send方法;那继续再来看看这个send方法的源码是怎么样的。

public void send(Message message) throws RemoteException {
        mTarget.send(message);
}

其实就是调用了刚刚proxy的send方法吧message当参数传进去。这里面的逻辑其实就是通过这个proxy类中的IBinder对象来远程调用service中已经实现的send方法。
一目了然Messenger就是一个典型的aidl的例子。

总结

  1. Server端就是Handler里面实现的MessengerImpl内部类,然后在Service里面被实例化了。而这个aidl也是只有一个方法(send(Message)),就是通过当前Handler来发送一个消息。
  2. Client端就是通过Service返回过来的IBinder类来获取一个proxy对象,通过proxy对象远程调用send方法来完成通讯。

补充:如果要实现Service那边处理完消息返回给activity的话只要在activity里面也创建一个Messenger,然后把这个Messenger通过Message赋值给参数message.replyTo传过去就好了,同样Service就可以通过这个参数里面的Messenger来发送消息给activity通过activity里面handler来处理消息来完成双向通讯。

注意:这里有一点就是如果不是跨进的的话Service和Activity都运行在主线程,那么Service中用于处理消息的Handler里面不能执行耗时的工作,不然会导致ActivityUI界面卡住,因为Handler是创建在Service线程(主线程)用的是主线程的Looper。如果是跨进程的话Activity这边的主线程就不会卡住(Service所在的线程会卡住)。因为在普通的aidl在proxy调用的时候(其实就是调用IBinder.transact方法时)会挂起当前线程因此在Service端执行耗时操作时activity的UI线程会卡住。而messenger和普通的aidl不同之处在于它又添加了一个Handler,而这个Handler是运行在Service所在线程(默认为Service所在进程的主线程)而真正的Messenger.send方法只执行了一个Handler的sengmessage方法(这个方法是运行在底层binder的工作线程中,只要在这个线程中不执行耗时操作调用方所在的线程就不会被挂起太久)。因此不会卡住(Service线程可能会卡住)。这一点我感觉IntentService的实现非常的相似。

Android 基于Message的进程间通信 Messenger完全解析

Android 基于Message的进程间通信 Messenger完全解析

1、概述

Binder能干什么?Binder可以提供系统中任何程序都可以访问的全局服务。这个功能当然是任何系统都应该提供的,下面我们简单看一下Android的Binder的框架

Android Binder框架分为服务器接口、Binder驱动、以及客户端接口;简单想一下,需要提供一个全局服务,那么全局服务那端即是服务器接口,任何程序即客户端接口,它们之间通过一个Binder驱动访问。

服务器端接口:实际上是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,会接收Binder驱动发送的消息,收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。

Binder驱动:该对象也为Binder类的实例,客户端通过该对象访问远程服务。

客户端接口:获得Binder驱动,调用其transact()发送消息至服务器

如果大家对上述不了解,没关系,下面会通过例子来更好的说明,实践是检验真理的唯一标准嘛

2、AIDL的使用

如果对Android比较熟悉,那么一定使用过AIDL,如果你还不了解,那么也没关系,下面会使用一个例子展示AIDL的用法。

我们使用AIDL实现一个跨进程的加减法调用

1、服务端

新建一个项目,创建一个包名:com.zhy.calc.aidl,在包内创建一个ICalcAIDL文件:

  1. package com.zhy.calc.aidl;  
  2. interface ICalcAIDL  
  3. {
  4.     int add(int x , int y);  
  5.     int min(int x , int y );  
  6. }

注意,文件名为ICalcAIDL.aidl

然后在项目的gen目录下会生成一个ICalcAIDL.Java文件,暂时不贴这个文件的代码了,后面会详细说明

然后我们在项目中新建一个Service,代码如下:

  1. package com.example.zhy_binder;  
  2. import com.zhy.calc.aidl.ICalcAIDL;  
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.os.RemoteException;  
  7. import android.util.Log;  
  8. public class CalcService extends Service  
  9. {
  10.     private static final String TAG = “server”;  
  11.     public void onCreate()  
  12.     {
  13.         Log.e(TAG, “onCreate”);  
  14.     }
  15.     public IBinder onBind(Intent t)  
  16.     {
  17.         Log.e(TAG, “onBind”);  
  18.         return mBinder;  
  19.     }
  20.     public void onDestroy()  
  21.     {
  22.         Log.e(TAG, “onDestroy”);  
  23.         super.onDestroy();  
  24.     }
  25.     public boolean onUnbind(Intent intent)  
  26.     {
  27.         Log.e(TAG, “onUnbind”);  
  28.         return super.onUnbind(intent);  
  29.     }
  30.     public void onRebind(Intent intent)  
  31.     {
  32.         Log.e(TAG, “onRebind”);  
  33.         super.onRebind(intent);  
  34.     }
  35.     private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()  
  36.     {
  37.         @Override  
  38.         public int add(int x, int y) throws RemoteException  
  39.         {
  40.             return x + y;  
  41.         }
  42.         @Override  
  43.         public int min(int x, int y) throws RemoteException  
  44.         {
  45.             return x – y;  
  46.         }
  47.     };
  48. }

在此Service中,使用生成的ICalcAIDL创建了一个mBinder的对象,并在Service的onBind方法中返回

*后记得在AndroidManifest中注册

  1. <service android:name=“com.example.zhy_binder.CalcService” >  
  2.            <intent-filter>  
  3.                <action android:name=“com.zhy.aidl.calc” />  
  4.                <category android:name=“android.intent.category.DEFAULT” />  
  5.            </intent-filter>  
  6.        </service>  

这里我们指定了一个name,因为我们一会会在别的应用程序中通过Intent来查找此Service;这个不需要Activity,所以我也就没写Activity,安装完成也看不到安装图标,悄悄在后台运行着。

到此,服务端编写完毕。下面开始编写客户端

2、客户端

客户端的代码比较简单,创建一个布局,里面包含4个按钮,分别为绑定服务,解除绑定,调用加法,调用减法

布局文件:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     xmlns:tools=“http://schemas.android.com/tools”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“match_parent”  
  5.     android:orientation=“vertical” >  
  6.     <Button  
  7.         android:layout_width=“fill_parent”  
  8.         android:layout_height=“wrap_content”  
  9.         android:onClick=“bindService”  
  10.         android:text=“BindService” />  
  11.     <Button  
  12.         android:layout_width=“fill_parent”  
  13.         android:layout_height=“wrap_content”  
  14.         android:onClick=“unbindService”  
  15.         android:text=“UnbindService” />  
  16.     <Button  
  17.         android:layout_width=“fill_parent”  
  18.         android:layout_height=“wrap_content”  
  19.         android:onClick=“addInvoked”  
  20.         android:text=“12+12” />  
  21.     <Button  
  22.         android:layout_width=“fill_parent”  
  23.         android:layout_height=“wrap_content”  
  24.         android:onClick=“minInvoked”  
  25.         android:text=“50-12” />  
  26. </LinearLayout>  

主Activity

  1. package com.example.zhy_binder_client;  
  2. import android.app.Activity;  
  3. import android.content.ComponentName;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.content.ServiceConnection;  
  7. import android.os.Bundle;  
  8. import android.os.IBinder;  
  9. import android.util.Log;  
  10. import android.view.View;  
  11. import android.widget.Toast;  
  12. import com.zhy.calc.aidl.ICalcAIDL;  
  13. public class MainActivity extends Activity  
  14. {
  15.     private ICalcAIDL mCalcAidl;  
  16.     private ServiceConnection mServiceConn = new ServiceConnection()  
  17.     {
  18.         @Override  
  19.         public void onServiceDisconnected(ComponentName name)  
  20.         {
  21.             Log.e(“client”, “onServiceDisconnected”);  
  22.             mCalcAidl = null;  
  23.         }
  24.         @Override  
  25.         public void onServiceConnected(ComponentName name, IBinder service)  
  26.         {
  27.             Log.e(“client”, “onServiceConnected”);  
  28.             mCalcAidl = ICalcAIDL.Stub.asInterface(service);
  29.         }
  30.     };
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState)  
  33.     {
  34.         super.onCreate(savedInstanceState);  
  35.         setContentView(R.layout.activity_main);
  36.     }
  37.     /** 
  38.      * 点击BindService按钮时调用 
  39.      * @param view 
  40.      */  
  41.     public void bindService(View view)  
  42.     {
  43.         Intent intent = new Intent();  
  44.         intent.setAction(“com.zhy.aidl.calc”);  
  45.         bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
  46.     }
  47.     /** 
  48.      * 点击unBindService按钮时调用 
  49.      * @param view 
  50.      */  
  51.     public void unbindService(View view)  
  52.     {
  53.         unbindService(mServiceConn);
  54.     }
  55.     /** 
  56.      * 点击12+12按钮时调用 
  57.      * @param view 
  58.      */  
  59.     public void addInvoked(View view) throws Exception  
  60.     {
  61.         if (mCalcAidl != null)  
  62.         {
  63.             int addRes = mCalcAidl.add(12, 12);  
  64.             Toast.makeText(this, addRes + “”, Toast.LENGTH_SHORT).show();  
  65.         } else  
  66.         {
  67.             Toast.makeText(this, “服务器被异常杀死,请重新绑定服务端”, Toast.LENGTH_SHORT)  
  68.                     .show();
  69.         }
  70.     }
  71.     /** 
  72.      * 点击50-12按钮时调用 
  73.      * @param view 
  74.      */  
  75.     public void minInvoked(View view) throws Exception  
  76.     {
  77.         if (mCalcAidl != null)  
  78.         {
  79.             int addRes = mCalcAidl.min(58, 12);  
  80.             Toast.makeText(this, addRes + “”, Toast.LENGTH_SHORT).show();  
  81.         } else  
  82.         {
  83.             Toast.makeText(this, “服务端未绑定或被异常杀死,请重新绑定服务端”, Toast.LENGTH_SHORT)  
  84.                     .show();
  85.         }
  86.     }
  87. }

很标准的绑定服务的代码。

直接看运行结果:

%title插图%num

我们首先点击BindService按钮,查看log

  1. 08-09 22:56:38.959: E/server(29692): onCreate
  2. 08-09 22:56:38.959: E/server(29692): onBind
  3. 08-09 22:56:38.959: E/client(29477): onServiceConnected

可以看到,点击BindService之后,服务端执行了onCreate和onBind的方法,并且客户端执行了onServiceConnected方法,标明服务器与客户端已经联通

然后点击12+12,50-12可以成功的调用服务端的代码并返回正确的结果

下面我们再点击unBindService

  1. 08-09 22:59:25.567: E/server(29692): onUnbind
  2. 08-09 22:59:25.567: E/server(29692): onDestroy

由于我们当前只有一个客户端绑定了此Service,所以Service调用了onUnbind和onDestory

然后我们继续点击12+12,50-12,通过上图可以看到,依然可以正确执行,也就是说即使onUnbind被调用,连接也是不会断开的,那么什么时候会端口呢?

即当服务端被异常终止的时候,比如我们现在在手机的正在执行的程序中找到该服务:

%title插图%num

点击停止,此时查看log

  1. 08-09 23:04:21.433: E/client(30146): onServiceDisconnected

可以看到调用了onServiceDisconnected方法,此时连接被断开,现在点击12+12,50-12的按钮,则会弹出Toast服务端断开的提示。

说了这么多,似乎和Binder框架没什么关系,下面我们来具体看一看AIDL为什么做了些什么。

3、分析AIDL生成的代码

1、服务端

先看服务端的代码,可以看到我们服务端提供的服务是由

  1. private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()  
  2.     {
  3.         @Override  
  4.         public int add(int x, int y) throws RemoteException  
  5.         {
  6.             return x + y;  
  7.         }
  8.         @Override  
  9.         public int min(int x, int y) throws RemoteException  
  10.         {
  11.             return x – y;  
  12.         }
  13.     };

ICalcAILD.Stub来执行的,让我们来看看Stub这个类的声明:

  1. public static abstract class Stub extends android.os.Binder implements com.zhy.calc.aidl.ICalcAIDL  

清楚的看到这个类是Binder的子类,是不是符合我们文章开通所说的服务端其实是一个Binder类的实例

接下来看它的onTransact()方法:

  1. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  2. {
  3. switch (code)  
  4. {
  5. case INTERFACE_TRANSACTION:  
  6. {
  7. reply.writeString(DESCRIPTOR);
  8. return true;  
  9. }
  10. case TRANSACTION_add:  
  11. {
  12. data.enforceInterface(DESCRIPTOR);
  13. int _arg0;  
  14. _arg0 = data.readInt();
  15. int _arg1;  
  16. _arg1 = data.readInt();
  17. int _result = this.add(_arg0, _arg1);  
  18. reply.writeNoException();
  19. reply.writeInt(_result);
  20. return true;  
  21. }
  22. case TRANSACTION_min:  
  23. {
  24. data.enforceInterface(DESCRIPTOR);
  25. int _arg0;  
  26. _arg0 = data.readInt();
  27. int _arg1;  
  28. _arg1 = data.readInt();
  29. int _result = this.min(_arg0, _arg1);  
  30. reply.writeNoException();
  31. reply.writeInt(_result);
  32. return true;  
  33. }
  34. }
  35. return super.onTransact(code, data, reply, flags);  
  36. }

文章开头也说到服务端的Binder实例会根据客户端依靠Binder驱动发来的消息,执行onTransact方法,然后由其参数决定执行服务端的代码。

可以看到onTransact有四个参数

code , data ,replay , flags

code 是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法

data客户端传递过来的参数

replay服务器返回回去的值

flags标明是否有返回值,0为有(双向),1为没有(单向)

我们仔细看case TRANSACTION_min中的代码

data.enforceInterface(DESCRIPTOR);与客户端的writeInterfaceToken对用,标识远程服务的名称

int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();

接下来分别读取了客户端传入的两个参数

int _result = this.min(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);

然后执行this.min,即我们实现的min方法;返回result由reply写回。

add同理,可以看到服务端通过AIDL生成Stub的类,封装了服务端本来需要写的代码。

2、客户端

客户端主要通过ServiceConnected与服务端连接

  1. private ServiceConnection mServiceConn = new ServiceConnection()  
  2.     {
  3.         @Override  
  4.         public void onServiceDisconnected(ComponentName name)  
  5.         {
  6.             Log.e(“client”, “onServiceDisconnected”);  
  7.             mCalcAidl = null;  
  8.         }
  9.         @Override  
  10.         public void onServiceConnected(ComponentName name, IBinder service)  
  11.         {
  12.             Log.e(“client”, “onServiceConnected”);  
  13.             mCalcAidl = ICalcAIDL.Stub.asInterface(service);
  14.         }
  15.     };

如果你比较敏锐,应该会猜到这个onServiceConnected中的IBinder实例,其实就是我们文章开通所说的Binder驱动,也是一个Binder实例

在ICalcAIDL.Stub.asInterface中*终调用了:

  1. return new com.zhy.calc.aidl.ICalcAIDL.Stub.Proxy(obj);  

这个Proxy实例传入了我们的Binder驱动,并且封装了我们调用服务端的代码,文章开头说,客户端会通过Binder驱动的transact()方法调用服务端代码

直接看Proxy中的add方法

  1. @Override public int add(int x, int y) throws android.os.RemoteException  
  2. {
  3. android.os.Parcel _data = android.os.Parcel.obtain();
  4. android.os.Parcel _reply = android.os.Parcel.obtain();
  5. int _result;  
  6. try {  
  7. _data.writeInterfaceToken(DESCRIPTOR);
  8. _data.writeInt(x);
  9. _data.writeInt(y);
  10. mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  
  11. _reply.readException();
  12. _result = _reply.readInt();
  13. }
  14. finally {  
  15. _reply.recycle();
  16. _data.recycle();
  17. }
  18. return _result;  
  19. }

首先声明两个Parcel对象,一个用于传递数据,一个用户接收返回的数据

_data.writeInterfaceToken(DESCRIPTOR);与服务器端的enforceInterfac对应

_data.writeInt(x);
_data.writeInt(y);写入需要传递的参数

mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

终于看到了我们的transact方法,*个对应服务端的code,_data,_repay分别对应服务端的data,reply,0表示是双向的

_reply.readException();
_result = _reply.readInt();

*后读出我们服务端返回的数据,然后return。可以看到和服务端的onTransact基本是一行一行对应的。

到此,我们已经通过AIDL生成的代码解释了Android Binder框架的工作原理。Service的作用其实就是为我们创建Binder驱动,即服务端与客户端连接的桥梁。

AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。那么我们是否可以不通过写AIDL来实现远程的通信呢?下面向大家展示如何完全不依赖AIDL来实现客户端与服务端的通信。

4、不依赖AIDL实现程序间通讯

1、服务端代码

我们新建一个CalcPlusService.java用于实现两个数的乘和除

  1. package com.example.zhy_binder;  
  2. import android.app.Service;  
  3. import android.content.Intent;  
  4. import android.os.Binder;  
  5. import android.os.IBinder;  
  6. import android.os.Parcel;  
  7. import android.os.RemoteException;  
  8. import android.util.Log;  
  9. public class CalcPlusService extends Service  
  10. {
  11.     private static final String DESCRIPTOR = “CalcPlusService”;  
  12.     private static final String TAG = “CalcPlusService”;  
  13.     public void onCreate()  
  14.     {
  15.         Log.e(TAG, “onCreate”);  
  16.     }
  17.     @Override  
  18.     public int onStartCommand(Intent intent, int flags, int startId)  
  19.     {
  20.         Log.e(TAG, “onStartCommand”);  
  21.         return super.onStartCommand(intent, flags, startId);  
  22.     }
  23.     public IBinder onBind(Intent t)  
  24.     {
  25.         Log.e(TAG, “onBind”);  
  26.         return mBinder;  
  27.     }
  28.     public void onDestroy()  
  29.     {
  30.         Log.e(TAG, “onDestroy”);  
  31.         super.onDestroy();  
  32.     }
  33.     public boolean onUnbind(Intent intent)  
  34.     {
  35.         Log.e(TAG, “onUnbind”);  
  36.         return super.onUnbind(intent);  
  37.     }
  38.     public void onRebind(Intent intent)  
  39.     {
  40.         Log.e(TAG, “onRebind”);  
  41.         super.onRebind(intent);  
  42.     }
  43.     private MyBinder mBinder = new MyBinder();  
  44.     private class MyBinder extends Binder  
  45.     {
  46.         @Override  
  47.         protected boolean onTransact(int code, Parcel data, Parcel reply,  
  48.                 int flags) throws RemoteException  
  49.         {
  50.             switch (code)  
  51.             {
  52.             case 0x110:  
  53.             {
  54.                 data.enforceInterface(DESCRIPTOR);
  55.                 int _arg0;  
  56.                 _arg0 = data.readInt();
  57.                 int _arg1;  
  58.                 _arg1 = data.readInt();
  59.                 int _result = _arg0 * _arg1;  
  60.                 reply.writeNoException();
  61.                 reply.writeInt(_result);
  62.                 return true;  
  63.             }
  64.             case 0x111:  
  65.             {
  66.                 data.enforceInterface(DESCRIPTOR);
  67.                 int _arg0;  
  68.                 _arg0 = data.readInt();
  69.                 int _arg1;  
  70.                 _arg1 = data.readInt();
  71.                 int _result = _arg0 / _arg1;  
  72.                 reply.writeNoException();
  73.                 reply.writeInt(_result);
  74.                 return true;  
  75.             }
  76.             }
  77.             return super.onTransact(code, data, reply, flags);  
  78.         }
  79.     };
  80. }

我们自己实现服务端,所以我们自定义了一个Binder子类,然后复写了其onTransact方法,我们指定服务的标识为CalcPlusService,然后0x110为乘,0x111为除;

记得在AndroidMenifest中注册

  1. <service android:name=“com.example.zhy_binder.CalcPlusService” >  
  2.            <intent-filter>  
  3.                <action android:name=“com.zhy.aidl.calcplus” />  
  4.                <category android:name=“android.intent.category.DEFAULT” />  
  5.            </intent-filter>  
  6.        </service>  

服务端代码结束。

2、客户端代码

单独新建了一个项目,代码和上例很类似

首先布局文件:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     xmlns:tools=“http://schemas.android.com/tools”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“match_parent”  
  5.     android:orientation=“vertical” >  
  6.     <Button  
  7.         android:layout_width=“fill_parent”  
  8.         android:layout_height=“wrap_content”  
  9.         android:onClick=“bindService”  
  10.         android:text=“BindService” />  
  11.     <Button  
  12.         android:layout_width=“fill_parent”  
  13.         android:layout_height=“wrap_content”  
  14.         android:onClick=“unbindService”  
  15.         android:text=“UnbindService” />  
  16.     <Button  
  17.         android:layout_width=“fill_parent”  
  18.         android:layout_height=“wrap_content”  
  19.         android:onClick=“mulInvoked”  
  20.         android:text=“50*12” />  
  21.     <Button  
  22.         android:layout_width=“fill_parent”  
  23.         android:layout_height=“wrap_content”  
  24.         android:onClick=“divInvoked”  
  25.         android:text=“36/12” />  
  26. </LinearLayout>  

可以看到加入了乘和除

然后是Activity的代码

  1. package com.example.zhy_binder_client03;  
  2. import android.app.Activity;  
  3. import android.content.ComponentName;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.content.ServiceConnection;  
  7. import android.os.Bundle;  
  8. import android.os.IBinder;  
  9. import android.os.RemoteException;  
  10. import android.util.Log;  
  11. import android.view.View;  
  12. import android.widget.Toast;  
  13. public class MainActivity extends Activity  
  14. {
  15.     private IBinder mPlusBinder;  
  16.     private ServiceConnection mServiceConnPlus = new ServiceConnection()  
  17.     {
  18.         @Override  
  19.         public void onServiceDisconnected(ComponentName name)  
  20.         {
  21.             Log.e(“client”, “mServiceConnPlus onServiceDisconnected”);  
  22.         }
  23.         @Override  
  24.         public void onServiceConnected(ComponentName name, IBinder service)  
  25.         {
  26.             Log.e(“client”, ” mServiceConnPlus onServiceConnected”);  
  27.             mPlusBinder = service;
  28.         }
  29.     };
  30.     @Override  
  31.     protected void onCreate(Bundle savedInstanceState)  
  32.     {
  33.         super.onCreate(savedInstanceState);  
  34.         setContentView(R.layout.activity_main);
  35.     }
  36.     public void bindService(View view)  
  37.     {
  38.         Intent intentPlus = new Intent();  
  39.         intentPlus.setAction(“com.zhy.aidl.calcplus”);  
  40.         boolean plus = bindService(intentPlus, mServiceConnPlus,  
  41.                 Context.BIND_AUTO_CREATE);
  42.         Log.e(“plus”, plus + “”);  
  43.     }
  44.     public void unbindService(View view)  
  45.     {
  46.         unbindService(mServiceConnPlus);
  47.     }
  48.     public void mulInvoked(View view)  
  49.     {
  50.         if (mPlusBinder == null)  
  51.         {
  52.             Toast.makeText(this, “未连接服务端或服务端被异常杀死”, Toast.LENGTH_SHORT).show();  
  53.         } else  
  54.         {
  55.             android.os.Parcel _data = android.os.Parcel.obtain();
  56.             android.os.Parcel _reply = android.os.Parcel.obtain();
  57.             int _result;  
  58.             try  
  59.             {
  60.                 _data.writeInterfaceToken(“CalcPlusService”);  
  61.                 _data.writeInt(50);  
  62.                 _data.writeInt(12);  
  63.                 mPlusBinder.transact(0x110, _data, _reply, 0);  
  64.                 _reply.readException();
  65.                 _result = _reply.readInt();
  66.                 Toast.makeText(this, _result + “”, Toast.LENGTH_SHORT).show();  
  67.             } catch (RemoteException e)  
  68.             {
  69.                 e.printStackTrace();
  70.             } finally  
  71.             {
  72.                 _reply.recycle();
  73.                 _data.recycle();
  74.             }
  75.         }
  76.     }
  77.     public void divInvoked(View view)  
  78.     {
  79.         if (mPlusBinder == null)  
  80.         {
  81.             Toast.makeText(this, “未连接服务端或服务端被异常杀死”, Toast.LENGTH_SHORT).show();  
  82.         } else  
  83.         {
  84.             android.os.Parcel _data = android.os.Parcel.obtain();
  85.             android.os.Parcel _reply = android.os.Parcel.obtain();
  86.             int _result;  
  87.             try  
  88.             {
  89.                 _data.writeInterfaceToken(“CalcPlusService”);  
  90.                 _data.writeInt(36);  
  91.                 _data.writeInt(12);  
  92.                 mPlusBinder.transact(0x111, _data, _reply, 0);  
  93.                 _reply.readException();
  94.                 _result = _reply.readInt();
  95.                 Toast.makeText(this, _result + “”, Toast.LENGTH_SHORT).show();  
  96.             } catch (RemoteException e)  
  97.             {
  98.                 e.printStackTrace();
  99.             } finally  
  100.             {
  101.                 _reply.recycle();
  102.                 _data.recycle();
  103.             }
  104.         }
  105.     }
  106. }

为了明了,我直接在mulInvoked里面写了代码,和服务端都没有抽象出一个接口。首先绑定服务时,通过onServiceConnected得到Binder驱动即mPlusBinder;

然后准备数据,调用transact方法,通过code指定执行服务端哪个方法,代码和上面的分析一致。

下面看运行结果:

%title插图%num

是不是很好的实现了我们两个应用程序间的通讯,并没有使用aidl文件,也从侧面分析了我们上述分析是正确的。

 

好了,就到这里,相信大家看完这篇博文,对aidl和Binder的理解也会更加深刻。

mac连接android真机进行调试

一、如何让mac系统识别android手机。

  1. 将手机通过数据线连接到mac电脑上面。
  2. 在终端里面输入命令system_profiler SPUSBDataType
    1. USB 3.0 Hi-Speed Bus:
    2.       Host Controller Location: Built-in USB
    3.       Host Controller Driver: AppleUSBXHCI
    4.       PCI Device ID: 0x9c31 
    5.       PCI Revision ID: 0x0004 
    6.       PCI Vendor ID: 0x8086 
    7.       Bus Number: 0x0a 
    8.         SAMSUNG_Android:
    9.           Product ID: 0x6860
    10.           Vendor ID: 0x04e8  (Samsung Electronics Co., Ltd.)
    11.           Version: 4.00
    12.           Serial Number: 3230d190c139609b
    13.           Speed: Up to 480 Mb/sec
    14.           Manufacturer: SAMSUNG
    15.           Location ID: 0x14100000 / 5
    16.           Current Available (mA): 500
    17.           Current Required (mA): 96

    在结果信息里面寻找手机的Vendor ID,上面信息中Vendor ID为0x04e8

  3. vi ~/.android/adb_usb.ini添加0x04e8
  4. 在终端,输入adb提示 command not found.需将 adb的路径加入到配置文件里,终端编辑 ~/.bash_profile文件

    export PATH=/Users/lizhengdong/applications/sdk/platform-tools/:$PATH

  5. 注意上面的sdk路径要填写本机实际的sdk路径
  6. 重启终端以使adb命令生效
  7. 重启adb服务,命令如下:
  8. adb kill-server
  9. adb start-server
  10. adb devices 就可以看到列表
  11. 重启eclipse,在eclipse重新调试程序,可以在可选择的设备里面发现android手机,如果没有,可以重新插拔数据线试一下。

mac开发android 安卓真机调试解决方案

1、确保你的android设备真正链接到电脑上了

所以不要以为随便拿一根线,能充电,就可以传递数据了,我就是这么傻傻的拿了根不能用的数据线联机调试了半天。

方法:下载一个 androidfiletransfer.dmg,安装之后,看看能不能读取手机数据,如果能够读取,好的,恭喜你,*步完成了。

2 、设置好你机器的环境变量

在Mac下开发Android,要想在终端利用命令行使用adb/android等命令时,需要配置一下环境变量。

步骤:

1.首先,假设你已经下载了Android SDK,解压后安装了adb。记住sdk文件夹路径。我个人的路径为/Users/diqun/Destop/adt-bundle-mac-x86_64/sdk/

注意:路径中不能有中文和空格,总之后面文件报错的话肯定是路径名有问题

2.在终端中输入命令,进入用户目录。

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. $cd ~

3.然后输入命令,该命令的作用是如果不存在.bash_profile文件,则创建该文件

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. $touch .bash_profile

4.然后输入命令,该命令的作用是用文本编辑器TextEdit打开.bash_profile文件。如果你是*次配置环境变量,则该文档应该是空的。

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. $open -e .bash_profile

5.下面要在该文件中加入下面的代码

1)将adb加入环境变量

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. export PATH=${PATH}:~/Destop/adt-bundle-mac-x86_64/sdk/platform-tools

2)将android/ddms/emulator/sqlite3等加入环境变量

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. export PATH=${PATH}:~/Destop/adt-bundle-mac-x86_64/sdk/tools

6.保存并退出TextEdit。关闭bash终端。

7.测试:

1)测试adb

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. $adb version

显示

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. Android Debug Bridge version 1.0.31

2)测试android

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. $android

会打开Android SDK Manager窗口

3 、

*步: 查看usb设备信息(我用的是魅族mx4)

在 终端输入:system_profiler SPUSBDataType

可以查看连接的usb设备的信息

比如我的usb信息如下(部分内容):

 MX4:

          Product ID: 0x0c02

          Vendor ID: 0x2a45

          Version: ff.ff

          Serial Number: 750ACKK34RHK

          Speed: Up to 480 Mb/sec

          Manufacturer: Meizu

          Location ID: 0x14200000 / 19

          Current Available (mA): 1000

          Current Required (mA): 192

          Extra Operating Current (mA): 0

其中的 vendor ID: 0x2a45 很重要,记下来

第二步: 创建、修改adb_usb.ini文件

输入: vi ~/.android/adb_usb.ini 命令,在打开的 adb_usb.ini文件中添加0x2a45, (i编辑,esc退出编辑,:wq保存退出)

然后请一定重启finder :鼠标单击窗口左上角的苹果标志–>强制退出–>Finder–>重新启动

第三步:重启adb

adb kill-server

adb start-server

adb devices

就可以看到列表了!

%title插图%num
%title插图%num

注意,出现上面的设备之后才算成功

如果显示的是unauthorized,就在开发者选项里撤销USB调试授权,重新连接电脑,点击授权。

如果ADT中log遇到下面的问题:

[2011-07-27 10:31:48 – DeviceMonitor]Adb connection Error:EOF
[2011-07-27 10:31:48 – DeviceMonitor]Connection attempts: 1
[2011-07-27 10:31:49 – DeviceMonitor]Connection attempts: 2
[2011-07-27 10:31:50 – DeviceMonitor]Connection attempts: 3
[2011-07-27 10:31:51 – DeviceMonitor]Connection attempts: 4

不要慌张,重启eclipse。

上面的步骤都完成之后,打开模拟器界面

%title插图%num%title插图%num

恭喜你,大工告成了。

Mac的android真机调试

这些步骤是在百度中查找的,当时记录下来了,过了个把月了,今天写出来,我也重温步骤。

1.找到android的SDK路径,一般是 /user/你的电脑名/Library/android/sdk路径。找不到?你可以在终端直接

open ./Library/android/sdk

2. 2.1 在终端输入 cd ~ 进入用户目录
2.2 touch .bash_profile

2.3 open -e .bash_profile (e 编辑文件 在文中加入sdk的路径

export PATH=${PATH}:~/Library/android/sdk/platform-tools

export PATH=${PATH}:~/Library/android/sdk/tools

%title插图%num

保存并退推出

2.4 测试adb

终端输入 adb version 显示下面的提示则为成功

Android Debug Bridge version 1.0.39

Version 0.0.1-4500957

Installed as ./Library/android/sdk/platform-tools/adb

3.创建,修改adb_usb.ini文件

终端输入 system_profiler SPUSBDataType

在信息中找到 Vendor ID

%title插图%num

把 Vendor ID记录下来

输入命令vi ~/.android/adb_usb.ini

在adb_usb.ini中输入设备的Vendor ID后,保存并退出(:wq)

%title插图%num

重新启动finder 强制退出

4.终端关闭重启adb

adbkill-server

adbstart-server

5. 在mac上安装android file transfer 安装后插上usb与手机相连.

6. 在关于手机里面找到一行版本号。连续点击会有倒计时出现说 将要开启开发者功能,点击直到提示开启开发者模式。

在更多设置里面找到开发者模式,开启usb调试功能,允许usb安装App。

7.运行android项目

%title插图%num

uni-app使用iPhone手机真机调试(window)

uni-app使用iPhone手机真机调试(window)
1. 在电脑上下载安装iTunes

%title插图%num

下载地址:https://pc.qq.com/search.html#!keyword=itunes

下载完成双击打开

%title插图%num

手机使用数据线连接电脑并信任电脑,确保itunes和手机连接成功。

%title插图%num

手机连接成功以后,Hbuilder会检测到手机

%title插图%num

2.运行uni-app项目,点击运行按钮,选择运行的设备(如果没有设备请查看第4条)
控制台会有提示

%title插图%num
3.编译完成 打开手机设置->通用 下拉到图片位置!

%title插图%num

点进去信任该应用

%title插图%num

点击这个图标运行就可以了

4.如果不显示设备器下载itool4更新驱动
下载地址:https://www.itools.cn/

%title插图%num

点击进去直接会更新系统,更新完以后打开Hbuilder会检测到手机。