博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Binder的工作机制浅析

Posted on 2017-07-01 21:25  行歌  阅读(1066)  评论(0编辑  收藏  举报

在Android开发中,Binder主要用于Service中,包括AIDL和Messenger,其中Messenger的底层实现就是AIDL,所以我们这里通过AIDL来分析一下Binder的工作机制。

一、在Android Studio中建立AIDL

首先,我们需要建立一个AIDL

1.在建立了对应的实现Parcelable接口的实体类和AIDL接口后,文件结构如下:

2.点击clean Project/reBuild Project,出现如下错误:提示无法找到Book实体类。

 3.解决方案

这个问题的出现是因为我还没有在build.gradle中对默认的sourceSets进行修改,默认情况下他指定的源码目录不包括aidl。

加入下面语句后同步build.gradle,再重建工程即可。

在app下的build.gradle添加:

系统自动生成的IBookManager

二、Binder原理分析 

通过Structure

我们可以看到这个系统生成这个接口文件包括一个静态抽象类Stub和两个方法getBookList()和addBook(),这两个方法很显然就是我们之前在IBookManager.aidl中声明的方法,此外它还为这两个方法用两个int来标示,从而在onTransact()方法中起到标示作用,如下所示:

而内部类Stub继承自Binder,在这个类内部又有一个代理类Proxy。接下来看这一段代码:

这里判断了客户端和服务端是否处于同一个进程中,如果处于同一个进程中,则方法调用不会走跨进程的transact方法,而如果处于不同的进程中,则需要通过其中的代理里proxy来完成。下面展现代理类

在这个代理类中的getBookList和addBook方法中调用transact方法来发起RPC(远程过程调用)请求,并将当前线程挂起,然后服务端的onTransact方法响应并执行,当RPC过程返回后,当前线程继续执行。

  从上述描述中我们可以得出Binder的大概工作方式了,但其中有两点需要特别说明:

1.其实刚才也已经有所提及,即当客户端发起远程调用时会将当前线程挂起直至服务端方法执行完毕后才继续执行,所以如果一个远程方法比较耗时的话,是不能在UI线程中发起远程请求的,需要我们开启一个子线程然后再去进行远程调用。

2.由于服务端的Binder方法是运行在Binder的线程池中的,所以我们需要以同步的方式去实现保证线程安全。

Binder的工作机制具体如下图所示:

三、Binder的两个重要方法

  上面我们提到Binder运行在服务端进程,那么如果服务端进程由于某种原因异常终止,这个时候服务端的Binder死亡,会导致我们的远程调用失败,并且我们还不知道Binder连接断裂了。所以我们需要有方法来提示我们Binder已经断裂,然后我们就可以重新绑定远程Service。

  为此,Binder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath我们可以给Binder设置一个死亡代理,当其死亡时我们就能知道从而重现发起连接。

  具体如下:

 

private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient(){

        @Override
        public void binderDied() {
            if(mBookManager==null)
                return;;
            mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
            mBookManager=null;
            //TODO:这里重现绑定远程Service
            
        }
    };

在客户单绑定远程服务成功后,给binder设置死亡代理。

 mService=IMessageBoxManager.Stub.asInterface(binder);
    binder.linkToDeath(mDeathRecipient,0);

其中linkToDeath的第二个参数是个标记位,这里我们直接设为0,。