我对Binder的理解是:解决Android平台IPC的一种方案。它的优点是传输性能较好、相对稳定、架构清晰、安全性好、使用简单。
要理解Binder,从它的架构方面入手比较轻松一点。
Binder
Binder优点
现有的Linux进程间通信:Socket、管道、消息队列、共享内存
- 性能上,Socket管道消息队列均拷贝2次数据,Binder拷贝1次数据,共享内存0次拷贝。性能上仅次于共享内存。
- 相对共享内存Binder使用C/S模式,架构清晰Client和Server责任分明,而且能处理好同步问题,稳定性和易用性好。只要实现Server和Client端就可以,调用代理对象如同调用本地实例对象一样简单。
- 在安全上为每个进程分配UID和PID,UID是进程的身份象征,能够处理权限问题。采用C/S架构模式,对使用服务的一方只暴露Client,Client端向Server发送消息后,Server根据权限策略去判断Client端的UID/PID是否有权限,达到管控的目的。
Binder通信原理
通常两个进程之间的内存是不共享的,每个进程都有自己的进程空间。每个进程的进程空间划分为用户空间和内核空间,用户空间不共享数据,内核空间进程间共享数据。
传统IPC
进程A用户空间->内核空间->进程B用户空间。进程A用户空间和内核空间均开辟一块内存缓冲区,把数据拷贝到内核空间缓冲区,然后进程B用户空间再开辟一块内存缓冲区,最后内核空间缓冲区把数据拷贝到进程B用户空间缓冲区,这样拷贝了2次数据完成一次传输。
Binder
Binder使用了内存映射技术简化了IPC过程中的数据拷贝次数。首先Binder驱动通过mmap()函数开辟一块物理内存缓冲区,然后将内核空间和接收进程的用户空间均映射到这块内存上,然后发送进程把用户空间的数据拷贝到内核空间,由于内核空间和接收进程用户空间有了内存映射,接收进程用户空间便拿到了发送进程发来的数据,这样只拷贝一次数据便完成了IPC。进程A用户空间->内核空间(内存映射)==进程B用户空间(内存映射)。
Binder的C/S模式
Binder架构中,客户端和服务端我通常这样理解:在一次通信中,发起方是客户端,接收方是服务端。例如进程A和B,A向B发起通信,A是客户端B是服务端;同样的B向A发起通信,则B是客户端A是服务端。
- Android中获取某个服务
SMr指ServiceManager,很多系统服务如AMS、PMS和WMS等均通过SMr获取,路径frameworks/base/core/java/android/os/ServiceManager.java
- Server进程要把自己注册到SMr进程中。注册过程中Server是客户端,SMr是服务端
- Client进程向SMr通信,SMr返回Client准备获取的Server的远程代理对象。获取服务过程中Client是客户端,SMr是服务端
- Client进程通过Binder驱动与Server进程通信。它们通信的过程中Client是客户端,Server是服务端
- 客户端和服务端的通信都是由Binder驱动完成的。其中Binder驱动位于内核空间,而Client、Server、SMr均在用户空间
Launcher点击图标启动App
- AMS把自己注册到SMr进程中。注册过程中AMS是客户端,SMr是服务端
- Launcher通过SMr获取AMS的IActivityManager远程Binder引用。Launcher是客户端,SMr是服务端
- Launcher持有AMS的IActivityManager远程Binder引用,向AMS发送
startActivity
消息。Launcher是客户端,AMS是服务端 - AMS通过Launcher保存在AMS的远程Binder引用,向Launcher的ApplicationThread发送
handlePauseActivity
消息。AMS是客户端,Launcher是服务端 - AMS通过Zygote进程fork出App所在进程,反射调用
ActivityThread.main
初始化ActivityThread和ApplicationThread并调用ActivityThread.attach
。 - App进程中ActivityThread调用
ActivityManager.getService()
获取AMS远程Binder引用(同2)。App是客户端,SMr是服务端 - App进程持有AMS的IActivityManager远程Binder引用,向AMS发送
attachApplication
的消息,保存App进程的ApplicationThread的远程Binder引用到AMS一份。App是客户端,AMS是服务端 - attachApplication方法中,AMS通过App保存在AMS的远程Binder引用,向App的ApplicationThread发送
bindApplication
消息,为App创建Application。AMS是客户端,App是服务端 - attachApplication方法中,AMS通过App保存在AMS的远程Binder引用,封装了LaunchActivityItem和ResumeActivityItem,向App的ApplicationThread发送
scheduleTransaction
消息,然后ApplicationThread先后完成Activity的创建、onCreate
、onStart
、onResume
生命周期的回调。AMS是客户端,APP是服务端 - 一次启动App的通信就算完成,但是中间经过了很多次的IPC,App和AMS频繁轮流充当客户端和服务端的角色。
Binder驱动如何完成Binder对象的传递?以App进程中创建完ApplicationThread后向AMS保存一份客户端ApplicationThread引用为例
- App进程中,AMS远程Binder引用调用
attachApplication(mAppThread, startSeq)
方法,目的是将App进程的ApplicationThread的远程Binder引用保存到AMS一份,这样AMS可以通过这个远程Binder引用持续到和App进程互动。保存的过程是:AMS远程代理.attachApplication(AT实例)->BinderProxy.attachApplication(AT实例)->AMS.attachApplication(AT远程Binder引用)
。AT实例如何变成AT远程Binder引用也就是BinderProxy的,这是Parcel.read/writeStrongBinder
底层做的处理。
- App进程中,AMS远程Binder引用调用
AIDL
AIDL结构图
AIDL代码
1 | package com.jialong.test.aidl; |
AndroidStudio经过build后生成的文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81package com.jialong.test.aidl;
public interface IMyAidlInterface extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.jialong.test.aidl.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.jialong.test.aidl.IMyAidlInterface";
public Stub() { this.attachInterface(this, DESCRIPTOR); }
public static com.jialong.test.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.jialong.test.aidl.IMyAidlInterface))) {
return ((com.jialong.test.aidl.IMyAidlInterface) iin);
}
return new com.jialong.test.aidl.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() { return this; }
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_sum: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sum(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jialong.test.aidl.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) { mRemote = remote; }
@Override
public android.os.IBinder asBinder() { return mRemote; }
public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; }
@Override
public int sum(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_sum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int sum(int a, int b) throws android.os.RemoteException;
}
AIDL结构还是比较简单,首先接口中声明了sum
方法。有一个内部类Stub,它是一个Binder,当客户端和服务端同进程时,客户端拿到的就是服务端的Stub实现类的对象本身;当它们不处于同一进程时,客户端拿到的是包装了服务端远程Binder引用的本地Proxy,客户端执行方法时,最终执行的均是这个远程Binder引用的transact方法。
服务端的远程Binder引用到底是什么?
对应同进程和跨进程两种情况,通过打印分别验证客户端从ServiceConnection.onServiceConnection拿到的IBinder类型的obj对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//Binder
public class IMyAidlInterfaceImpl extends IMyAidlInterface.Stub {
@Override
public int sum(int a, int b) throws RemoteException {return 0;}
}
//Service#onBind
public Binder onBind(){
Binder b = new IMyAidlInterfaceImpl() ;
Log.i("onBind", b.getClass().getName() + ",hash:" + b.hashCode() + ",pid:" + android.os.Process.myPid());
return binder;
}
//Activity#ServiceConnection
ServiceConnection connection = new ServiceConnection(){
public void onServiceConnection(ComponentName component, Binder binder){
Log.i("onServiceConnected", binder.getClass().getName() + ",hash:" + binder.hashCode() + ",pid:" + android.os.Process.myPid());
}
}
打印结果:1
2
3
4
5
6同进程:
onBind: com.jialong.test.aidl.IMyAidlInterfaceImpl,hash:133484684,pid:4238
onServiceConnected: com.jialong.test.aidl.IMyAidlInterfaceImpl,hash:133484684,pid:4238
跨进程:
onBind: com.jialong.test.aidl.IMyAidlInterfaceImpl,hash:62475402,pid:6106
onServiceConnected: android.os.BinderProxy,hash:21405437,pid:6081
结论:同进程直接返回Binder对象;跨进程时客户端拿到的是BinderProxy对象。
BinderProxy.transact
BinderProxy.transact
最后调用transactNative
底层方法,通过Binder驱动回调在服务端进程的Stub.onTransact
方法。服务端Stub.onTransact
根据传递过来代表方法的整形ID值,决定调用哪个方法。
为什么客户端调用BinderProxy.transact服务端Stub.onTransact能响应?
客户端和服务端定义相同的AIDL,通过定义相同的DESCRIPTOR
变量确定相同的路径,然后通过定义的整形ID来保证onTransact
准确调用正确的方法。
Stub.asInterface作用
asInterface
用来将IBinder类型的参数obj转换成客户端所需要的AIDL对象,obj可能是本地对象或远程代理。当客户端和服务端处于相同进程,obj是AIDL类型,则直接把这个IBinder强转成AIDL类型然后返回;如果是不同进程,obj是BinderProxy类型,则obj被客户端本地Proxy包装然后返回Proxy。1
2
3
4
5
6
7
8public static IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) { return null; }
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IMyAidlInterface))) {
return ((IMyAidlInterface) iin);
}
return new IMyAidlInterface.Stub.Proxy(obj);
}
queryLocalInterface
方法判断是否为本地AIDL对象。AIDL对象的父类Binder在方法中判断DESCRIPTOR
相同则返回自己本身;而BinderProxy重写了这个方法直接返回空。
AIDL过程
- 使用上面的IMyAidlInterface为例
- 客户端通过ServiceConnection绑定服务,
onServiceConnected
回调服务端的IBinder对象obj(obj分2种情况:服务端的AIDL实例、BinderProxy实例),使用Stub.asInterface(obj)
得到能和服务端通信的Binder引用:- obj是AIDL实例代表客户端和服务端同进程,则直接强转成AIDL接口,后面的调用便和跨进程通信无关;
- obj是BinderProxy代表客户端和服务端跨进程,则返回
Stub.Proxy(BinderProxy)
,后面实际是BinderProxy在处理。
- 客户端调用Proxy.add->mRemote.transact(
Method_ID
, data, reply, flag)发出远程调用请求,把请求参数放入data中。BinderProxy.transact
->BinderProxy.transactNative
->Binder驱动native层
->服务端Stub.onTransact
。- 服务端根据
DESCRIPTOR
找到相对应的AIDL执行Stub.onTransact
,然后判断Method_ID
调用AIDL接口实现类的对应方法,如果有返回值则把结果保存到reply中,最后返回Binder驱动;
- Binder驱动调用完毕继续往下执行客户端的Proxy.add方法,从reply中读取服务端写好的返回值。完毕。
AMS和AT
AMS-ActivityManagerService
AMN(ActivityManagerNative)继承Binder并实现了IActivityManager接口,AMS继承于AMN并实现了IActivityManager接口方法,AMN内部类AMP(ActivityManagerProxy)实现了IActivityManager接口并维护一个IBinder对象remote,IActivityManager继承于IInterface接口。站在AIDL的角度,IActivityManager便是AIDL接口,AMS是IActivityManager实现类,AMN相当于Stub角色,AMP相当于Proxy角色。AT-ApplicationThread
类似于AMS。ATN(ApplicationThreadNative)继承Binder并实现了IApplicationThread接口,AT继承于ATN并实现了IApplicationThread接口方法,ATN内部类ATP(ApplicationThreadProxy)实现了IApplicationThread接口并维护一个IBinder对象remote,IApplicationThread继承于IInterface接口。站在AIDL的角度,IApplicationThread便是AIDL接口,AT是IApplicationThread实现类,ATN相当于Stub角色,ATP相当于Proxy角色。从8.0.0开始,Android中AMS和AT被改造成了AIDL的形式,AMS直接继承IActivityManager.Stub并弃用AMN剔除AMP,AT直接继承IApplicationThread.Stub剔除ATN和ATP,更容易从AIDL角度理解AMS和AT。
参考优秀文章:
Binder系列 [http://gityuan.com/2015/10/31/binder-prepare/#%E4%B8%80%E6%A6%82%E8%BF%B0]
Android Binder设计与实现 - 设计篇 [https://blog.csdn.net/universus/article/details/6211589]