零、Binder的传递
Android系统中,存在大量的 IPC 交互,同时也使用了大量的 Binder,那么Binder是怎么在各进程中进行对象的传递?
一、调用系统服务时,Binder的传递
回忆一下,Android系统的启动流程:Kernel——>Init进程——>Zygote——>SystemServer,在SystemServer启动时,会启动大量的系统级服务:AMS,PMS,WMS,InputManagerService等。
这些系统服务会注册到ServiceManager上,客户端是从ServiceManger中拿到要使用的系统服务对象,再调用服务的方法。
SystemServer 想要把服务注册到 ServiceManger 上:先拿到 IServiceManager 的proxy引用,调用 addService 方法将服务注册到 ServiceManager 的。
当客户端想调用系统服务时,通常会有以下写法:
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
往getSystemService方法传入一个字符串,返回服务的一个对象,然后就可以使用了。
客户端与 ServiceManager 处于两个进程中,想从 ServiceManager 中拿到服务对象,就涉及到 IPC 调用。其实也是客户端首先拿到 IServiceManager的proxy对象,调用getService() 方法取得服务对象的。
总结一下:
- SystemServer 中运行的服务要注册到 ServiceManager上。即 IPC1
- 客户端从 ServiceManager 中获取服务对象。即 IPC2
- 客户端拿到服务对象,在调用的时候,客户端使用的是 Binder对象的 proxy,实际运行的 Binder 在 SystemServer 进程中。即 IPC3
二、AIDL使用时的Binder传递
回忆下AIDL的使用:
- 定义AIDL 文件
- 编译生成 Stub 模板代码
- 定义服务端 Service,公布 Binder对象
- 定义客户端 使用bindService方法,在回调中拿到 Binder的 proxy对象
问题就在于,服务端 return的 Binder 对象是怎么传到 客户端的?
具体的代码调用流程就不贴了,画了一张图,使流程简化些讲解。
bindService(serviceIntent, new ServiceConnection() ...
客户端调用 bindService,传入一个 ServiceConnection 对象,在 ContextImpl.bindServiceCommon 中,传入到 ServiceDispather,伪代码如下:
class ServiceDispather {
ServiceConnection conn;
InnerConnection innerConn; // 为AMS回调,埋下伏笔
}
class InnerConnection extends IServiceConnection.Stub
InnerConnection 继承自IServiceConnection.Stub,为AMS回调 Binder 预留接口,AMS为客户端,InnerConnection是服务端。
最终客户端收 Binder对象时,也就是AMS调用 onConnected 时,将 Binder 通过 InnerConnection 传到客户端,再转发到 ServiceConnection中。
AMS收到bindService() 请求,调 requestBinding 将请求传到 服务端,在服务端的 onBind() 方法中 return Binder对象,委托AMS发布此Binder,AMS中存有 InnerConnect的代理对象,将Binder传到客户端去,ServiceDispather 调用 ServiceConnection 暴露 Binder对象给上层应用。