本文用于记录Binder机制的相关知识总结。简单讲,Binder是Android跨进程通信方式。进程本质是一个对象,对应着不同的虚拟机,也就是不同的存储空间。跨进程的通讯即不同存储空间如何通讯。
在学习之前有以下疑问:
- 如何知道客户端需要调用哪个进程以及该进程中的函数?
- 客户端如何将函数形参发送给远程进程中的函数,以及如何将远程进程函数计算结果返回客户端?
- 如何去屏蔽底层通信细节,让实现客户端调用远程函数就像调用本地函数一样?
- 底层通讯的原理是什么?
比如以下代码,我们知道WindowManager和APP处于不同的进程,系统服务都是运行在systemServer进程中,那APP如何能直接调用WindowManager的addView功能?
//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
这里我们要先了解WindowManager是怎么样的一个存在?
在Android开机启动过程中,Android会初始化系统的各种Service(AMS,WMS,PMS),并将这些Service向ServiceManager注册(即让ServiceManager管理)。客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,然后通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。
所以从上面的代码来看WindowManager的获取就是通过getSystemService(getApplication().WINDOW_SERVICE)
,原理就是向ServiceManager查询标识符为getApplication().WINDOW_SERVICE
的远程对象的引用。这个是WindowManager对象的引用,这个引用的真正实现是WindowManager的某个代理。得到这个引用后,在调用addView时,真正的实现是在代理里面,代理把参数打包到Parcel对象中,然后调用transact函数(该函数继承自Binder),再触发Binder驱动的一系列调用过程。
对于我们自定义一个进程来处理事务的情况(比如Actitivy需要其他进程Service计算某个结果),大概的步骤如下:
1.Activity通过bindService绑定Service。
2.在ServiceConnect这个绑定回调中获取Binder的引用mRemote。
3.Activity调用mRemote的transact,传递给他的数据和回调的数据引用。
4.Service内部创建一个Binder,这个Binder会通过onBinder方法的返回值给到Activity。
5.Service的Binder通过onTransact实现计算的功能(根据code执行指定的服务函数)。
6.通过数据引用获取到结果。
这里需要注意的是:
调用mRemote的transact方法会陷入内核态,也就是说剩下的都是由系统完成的,binder驱动会挂起当前线程,将参数包裹发给服务端程序(Service),所以尽量不要跨进程处理耗时的任务。
在服务端的onTransact(code, data, reply, flags)函数里面读取出包装的数据进行处理,然后把执行的结果放入客户端提供的reply包裹中,然后服务端向Binder驱动发送一个notify消息,唤醒客户端线程,继续执行使得客户端线程从Binder驱动返回到客户端代码区,再次回到用户态。
Binder繁琐的流程在于处理data和reply。AIDL(Android Interface Definition Language)就是简化了这个流程,编译器通过*.aidl文件的描述信息生成符合通信协议的Java代码。
AIDL类似是一个接口,让客户端和服务端知道了交互的方法,Activity直接调用。