Binder的使用和上层原理
简单介绍
Binder是Android中的一个类,实现了IBinder接口。从IPC角度说,Binder是Andoird的一种跨进程通讯方式。从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager·、WindowManager)和相应ManagerService的桥梁。从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService时,服务端返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务器端提供的服务或者数据(包括普通服务和基于AIDL的服务)。
Android中Binder主要用于Service,包括AIDL和Messenger。普通Service的Binder不涉及进程间通信,Messenger的底层其实是AIDL,所以下面通过AIDL分析Binder的工作机制。
由系统根据AIDL文件自动生成.java文件
- Book.java
表示图书信息的实体类,实现了Parcelable接口。 Book.aidl
Book类在AIDL中的声明。1
2package tk.thinkerzhangyan.ipcapplication;
parcelable Book;IBookManager.aidl
1
2
3
4
5
6
7package tk.thinkerzhangyan.ipcapplication;
import tk.thinkerzhangyan.ipcapplication.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
定义的管理Book实体的一个接口,包含getBookList和addBook两个方法,虽然IBookManager接口和Book实体在一个包下面,但是还是需要用import进行引入。
系统为IBookManager.aidl生产的Binder类,在gen目录下的IBookManager.java类。
IBookManager继承了IInterface
接口,所有在Binder中传输的接口都需要继承IInterface接口。结构如下:
- 声明了
getBookList
和addBook
方法,还声明了两个整型id分别标识这两个方法,用于标识在transact过程中客户端请求的到底是哪个方法。 - 声明了一个内部类
Stub
,这个Stub
就是一个Binder类,当客户端和服务端位于同一进程时,方法调用不会走跨进程的transact
。当二者位于不同进程时,方法调用需要走transact
过程,这个逻辑有Stub
的内部代理类Proxy
来完成。 - 这个接口的核心实现就是它的
内部类Stub
和Stub的内部代理类Proxy
。
Stub和Proxy类的内部方法和定义
- DESCRIPTOR
Binder的唯一标识,一般用Binder的类名表示。 - asInterface(android.os.IBinder obj)
将服务端的Binder对象转换为客户端所需的AIDL接口类型的对象,如果C/S位于同一进程,此方法返回就是服务端的Stub对象本身,否则返回的就是系统封装后的Stub.proxy对象。 - asBinder
返回当前Binder对象。 - onTransact
这个方法运行在服务端的Binder线程池中,由客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。该方法的原型是java public Boolean onTransact(int code,Parcelable data,Parcelable reply,int flags)
Ⅰ· 服务端通过code确定客户端请求的目标方法是什么,
Ⅱ· 接着从data取出目标方法所需的参数,然后执行目标方法。
Ⅲ·执行完毕后向reply写入返回值(如果有返回值)。
Ⅳ·如果这个方法返回值为false,那么服务端的请求会失败,利用这个特性我们可以来做权限验证,毕竟我们不希望每一个进程都能远程调用我们的服务。 - Proxy#getBookList和Proxy#addBook
这两个方法运行在客户端,当客户端远程调用这两个方法的时候,其内部实现过程是这样的:
Ⅰ· 首先创建该方法所需要的输入型对象Parcel对象_data,输出型Parcel对象_reply和返回值对象List。
Ⅱ· 然后把该方法的参数信息写入_data(如果有参数)
Ⅲ· 接着调用transact方法发起RPC(远程过程调用),同时当前线程挂起,
Ⅳ· 然后服务端的onTransact方法会被调用直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果,最后返回_reply中的数据。
之所以提供AIDL文件,是为了方便系统为我们生成代码,我们完全可以自己实现Binder,AIDL的本质,是系统为我们提供了一种快速生成Binder的工具。
注意以下两点:
- 当客户端发起远程请求的时候,当前的线程会被挂起,直到服务器端的远程进程返回结果,所以如果一个远程方法比较耗时的话,那么不能在UI线程中发起此远程请求。
- 由于服务器端的Binder方法运行在服务器端的Binder线程池中,所以Binder方法不管是否耗时,都应该采用同步的方式实现,因为它有已经运行在一个线程中了。
给Binder设置一个死亡代理
Binder运行在服务器端的进程,当服务器端的进程由于某些原因异常终止,我们到服务器端的Binder链接断裂,这个时候如果我们不知道连接断开,那么由于远程调用失败,服务器端的功能将会受到影响,我们可以通过给Binder设置死亡代理来解决这个问题。步骤如下:
声明一个DeathRecipient对象。DeathRecipient只有一个方法binderDied,当Binder死亡的时候,系统就会回调DeathRecipient方法,这个时候我们就可以重新发起连接请求从而恢复连接。
1
2
3
4
5
6
7
8
9
10
11private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
@Override
public void binderDied(){
if(mBookManager == null){
return;
}
mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mBookManager = null;
// TODO:接下来重新绑定远程Service
}
}Binder有两个很重要的方法linkToDeath和unlinkToDeath。通过linkToDeath为Binder设置一个死亡代理。
1
2mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient,0);
另外,可以通过Binder的isBinderAlive判断Binder是否死亡。
总结自:《Android开发艺术探索》