Binder简单归纳总结

我对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次数据完成一次传输。
    ipc_binder

  • Binder
    Binder使用了内存映射技术简化了IPC过程中的数据拷贝次数。首先Binder驱动通过mmap()函数开辟一块物理内存缓冲区,然后将内核空间和接收进程的用户空间均映射到这块内存上,然后发送进程把用户空间的数据拷贝到内核空间,由于内核空间和接收进程用户空间有了内存映射,接收进程用户空间便拿到了发送进程发来的数据,这样只拷贝一次数据便完成了IPC。进程A用户空间->内核空间(内存映射)==进程B用户空间(内存映射)。
    ipc_binder

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
    client_server_smr_binder
    • Server进程要把自己注册到SMr进程中。注册过程中Server是客户端,SMr是服务端
    • Client进程向SMr通信,SMr返回Client准备获取的Server的远程代理对象。获取服务过程中Client是客户端,SMr是服务端
    • Client进程通过Binder驱动与Server进程通信。它们通信的过程中Client是客户端,Server是服务端
    • 客户端和服务端的通信都是由Binder驱动完成的。其中Binder驱动位于内核空间,而Client、Server、SMr均在用户空间
  • Launcher点击图标启动App
    launcher_ams_app_smr_in_binder

    • 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的创建、onCreateonStartonResume生命周期的回调。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底层做的处理。

AIDL

AIDL结构图

AIDL_class_system

AIDL代码
1
2
3
4
5
package com.jialong.test.aidl;

interface IMyAidlInterface {
int sum(int a, int b);
}

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
81
package 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
8
public 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]