Binder(机制)
首先Binder是一个类,实现了IBinder接口,其次从IPC角度来看,就是Android中跨进程通信机制。
为什么使用Binder作为跨进程机制?
Linux中使用管道(pipe)做为进程之间的通信方式,虽然Android内核也是使用的Linux,但是作为移动端,性能和内存角度考虑,使用Binder是最好的方式。管道和Socket数据拷贝的次数为2次,文件共享0次,但是其安全性低太多,Binder数据拷贝次数为1次,也能为发送端提供身份验证。
这是罗升阳博客Android系统Binder机制中的四个组件Client、Server、Service Manager和Binder驱动程序中的关系图
Binder机制涉及到最重要的四个组件Client、Server、Service Manager和Binder驱动设备
Client和Server都是在应用层实现的,Server、Client都是运行在独立的进程中,两者之间的通信就是IPC的方式,其中就要通过Service Manager和Binder驱动设备。
- Client:客户端(调用IServiceManager::getService和Binder驱动程序交互)
- Server: 服务端(调用IServiceManager::addServcie和Binder驱动程序交互)
- Service Manager:实际也是Server,更是一种守护线程,管理Server,并向Client提供查询Server接口
- Binder驱动:搭建进程之间通信的建立,并且支持data数据包的传输
举个栗子 这里使用MediaService
MediaService
在native层可以找到MediaService的源码:
int main(int argc, char** argv)
{
//获得一个ProcessState实例
sp<ProcessState> proc(ProcessState::self());
//得到一个ServiceManager对象
sp<IServiceManager> sm = defaultServiceManager();
MediaPlayerService::instantiate();//初始化MediaPlayerService服务
ProcessState::self()->startThreadPool();//看名字,启动Process的线程池?
IPCThreadState::self()->joinThreadPool();//将自己加入到刚才的线程池?
}
最重要的ServiceManager是通过defaultServiceManager()获取到的,中间还要获取ServiceManager的Binder代理,实在太复杂,不想多说,然后初始化MediaPlayerService服务,也是打开Binder驱动设备,设置looper循环。
ServiceManager存在的意义:
Server先add到ServiceManager中几种管理,Client想要和Server交互,需要先到ServiceManager中查询Service信息,然后通过SM返回的东西和Server交互
在ServiceManager中其源码service_manager知道了三点作用:
- 打开了Binder设备
- 告诉Binder驱动设备,自己就是Binder的上下文管理者,也就是守护线程
- 进入一个无线循环,充当Server的角色
Binder的使用场景:
1 Activity启动
Activity的启动需要用到ActivityManagerService,但是我们的App进程和ActivityManagerService所在的进程不是同一个进程,所以就需要用到进程间通讯了。在App进程中我们拿到的是ActivityManagerService的一个分身,也就是ActivityManagerProxy,这个ActivityManagerProxy与ActivityManagerService都实现了IActivityManager接口,因此它们具有相同的功能,但是ActivityManagerProxy只是做了一个中转,创建两个Parcel对象,一个用于携带请求的参数,一个用于拿到请求结果,然后调用transact方法,通过Binder驱动,ActivityManagerService的onTransact方法会被调用,然后根据相应的code,调用相应的方法,并把处理结果返回。
在这个过程中,我们的App进程就是Client,ActivityManagerService所在的进程是Service。
但是Activity的启动过程还没有完,ActivityManagerService还会调用我们App所在进程的ApplicationThread来最终完成Activity的启动,其实ActivityManagerService拿到的也是ApplicationThread的一个分身ApplicationThreadProxy,通过这个分身,ApplicationThread相应的方法会被调用。
在这个过程中,我们的App端是Server,ActivityManagerService所在的进程是Client。
还有一个问题我们要注意,ActivityThread有一个内部类H(一个Hander),ApplicationThread方法内部都会通过这个Handler来发送消息,最终调用到ActivityThread的方法。为什么要这么做呢?
在分析源码的过程中,很长一段时间,这个问题都困扰着我,直到有一天对Binder的理解加深了,我才明白:Binder服务端的方法都是运行在Binder线程池的一个线程中的,所以要通过Hander,把方法的调用切换到主线程中来。
2 Intent传递数据
我们都知道Intent可以传递的数据包含:基本类型、String、实现了Serializable接口或者Parcelable接口的类以及对应的数组或者集合类。其实Intent中的数据都是通过Bundle来携带的,那么我们就要有个疑问了,为什么限定只能是这些类型的数据,而不是任意的数据类型呢?
归根结底,限制这些类型的是Parcel这个类。如果我们查看源码的话就会看到,Bundle其实也是用到了Parcel这个类。
Parcel
,“包裹的意思”,它的作用就是为了在IPC过程中存放数据。我们要知道一点,进程间传递数据,实际上就是二进制数据,所以对于非基本类型,必然存在着序列化和反序列过程,这也是为什么要求Intent传递的非基本类型数据必须实现Serializable或者Parcelable接口的原因。至于Parcel在IPC过程中使用到的地方,我们可以看一段代码,这个是我仿造着AIDL生成的文件,自己手写的一个Binder服务端。看一下Proxy类的add方法,实际上就是先创建两个Parcel对象,一个通过调用
writexxx 方法用于存放请求数据,一个是通过调用 readxxx
方法获取结果。Proxy真正干的就是这些,真正计算的还是服务端Stub的实现类。当Proxy调用
mRemote.transact(TRANSACTION_add, _data, _reply, 0);
方法后,Stub的onTransact方法会被调用,进而调用真正的add方法。