Android中的Binder与AIDL

一、Binder简析

直观来说,Binder 是 Android 中的一个类,是继承了 IBinder 接口;从 IPC 角度考虑 Binder 是进程间通信的一种方式;从 Framework 层,Binder 是 ServiceManager 用来连接各种 Manager(AM,WM) 和各种 ManagerService 的桥梁;从应用层来说,Binder 是客户端和服务器端进行通信的媒介,当 bindService 的时候服务器端会返回一个包含了服务端业务调用的 Binder 对象,客户端就可以获取服务端提供的数据或服务。

1.1 为什么要使用 Binder 来进行进程间通信

  1. 传统进程间通信机制如 Socket 开销较大且效率不高
  2. 管道和队列拷贝次数太多
  3. 移动设备的安全性,传统通信机制安全新低,大部分情况接收方无法得到发送方进行的 PID/UID,难以进行身份验证
  4. binder 在设计时就考虑到了以上的问题,在保证效率的同时也提高了安全性

二、Binder 在应用层的使用 - AIDL

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\MyApplication\\ServiceDemo\\app\\src\\main\\aidl\\com\\renxl\\servicedemo\\aidl\\MyWorker.aidl
*/
package com.renxl.servicedemo.aidl;
// Declare any non-default types here with import statements

public interface MyWorker extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.renxl.servicedemo.aidl.MyWorker {
private static final java.lang.String DESCRIPTOR = "com.renxl.servicedemo.aidl.MyWorker";

/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.renxl.servicedemo.aidl.MyWorker interface,
* generating a proxy if needed.
*/
public static com.renxl.servicedemo.aidl.MyWorker asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.renxl.servicedemo.aidl.MyWorker))) {
return ((com.renxl.servicedemo.aidl.MyWorker) iin);
}
return new com.renxl.servicedemo.aidl.MyWorker.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_doWork: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.doWork(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.renxl.servicedemo.aidl.MyWorker {
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 doWork(java.lang.String str) 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.writeString(str);
mRemote.transact(Stub.TRANSACTION_doWork, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

static final int TRANSACTION_doWork = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public int doWork(java.lang.String str) throws android.os.RemoteException;
}

2.1 介绍

我们自定义的 AIDL 数据类继承 IInterface 接口,因为可以在 Binder 中传输的数据都需要继承 IInterface 并实现其 asBinder 方法

Stub 是 AIDL 接口中的抽象类,Stub 类继承了 Binder 和我们需要在进程中传递的数据类型接口,Stub 类中还有 Proxy 子类,该类也继承了需要在进程间传递的数据类型

2.2 服务端的返回值

从客户端通过绑定形式启动 Service 时服务端返回的客户端所需的 AIDL 接口类型的对象入手,开发中服务端返回的 Binder 对象的类继承了 AIDL 中内部类 Stub 类,因为 Java 中多态的存在,我们需要 IBinder 对象时返回 IBinder 的子类即可。说一下为什么 服务端 返回的是 AIDL 中的内部类 Stub 类的对象的子类,因为,该抽象类的子类实现了我们定义的 .aidl 接口中的方法,所以返回的应该是这个 Stub 类的子类。Stub 子类实现了 .aidl 中声明的方法的抽象方法。

服务端 Stub 的子类实例化时,因为同时是 Binder 的子类,会调用 Binder 的 attachInterface 方法,将本身和 IInterface 的描述字符串添加到 Binder 类中,Binder 类中会根据描述字符串匹配保存当前的 Binder 对象。

2.3 进程间通信时客户端工作流程

根据绑定模式的 Service 绑定过程,最终服务端返回的 Binder 对象会传递到客户端的 ServiceConnection 的 onServiceConnected 方法中。

客户端得到服务端返回的 Binder 后,会通过自定义 AIDL 接口类的 asInterface 将该 Binder 转化为客户端需要的接口类型对象,asInterface 方法会从 Binder 类中根据描述字符串寻找相应的 IInterface,客户端和服务端不在同一进程时,由于 Binder 类的加载也是分开的,所以在客户端的 Binder 中不会找到匹配的 IInterface

在同进程请求时 asInterface 返回的是 Stub 的实现类的对象,也就是直接返回了服务端返回的继承了 Stub 类的对象。同进程不用考虑 Binder 传输数据,所以直接调用 Stub 实现类的各种方法即可实现客户端调用服务端的数据和服务的功能。

在跨进程调用时,由于客户端 Binder 中不能找到对应的 IInterface,所以 asInterface 中返回的是根据 Stub 的子类对象构造的 Stub.Proxy 类的对象。Stub.Proxy 类中调用服务器端方法时会将参数和返回值都使用序列化处理,经过跨进程通信后最终将结果返回到客户端。从而完成客户端调用服务端服务和数据的功能。

为什么服务器端返回的 Binder 是客户端需要的类对象,但是跨进程时需要转换为 Proxy ?

因为不同进程间的对象不能相互调用,虽然绑定完成时客户端拿到了服务器端对象的引用但是并不能直接操作该对象,所以需要通过 Proxy 来代理,Proxy 类的方法执行时会在 Native 层通过系统进程的协助调用服务端对象来执行任务,所以跨进程时客户端拿到的 Binder 对象不可以直接强制转型为需要的对象。

2.4 进程间通信时服务端的工作流程

跨进程调用时客户端的调用逻辑:上面说了跨进程时得到的AIDL接口数据类型是 Stub.Proxy ,所以直接分析 Stub.Proxy 类中的方法调用。Stub.Proxy 中调用方法并不会直接调用服务端的方法,会先创建 Parcel 类型的输入对象和输出对象以及返回值。如果方法有参数就将参数写入输入型 Parcel 对象中,这个过程还是在客户端,接下来会将序列化后的参数,序列化的返回值,以及表示客户端调用的是哪个方法的 int 值传入 Binder 的 transact 方法,客户端线程挂起。

Binder 的 transact 方法中会调用 Native 层的方法,Native 层会根据 IInterface 描述字符串从系统中所有进程的 Binder 中找到客户端 Proxy 对应的服务器端的 IInterface ,然后通过其 asBinder 方法得到服务器端的 Binder 对象也就是 Stub 对象,再通过调用其 onTransact 方法执行客户端指定的操作。这个过程由系统进程负责将客户端跟服务端连接。

说一下这个 Binder , 创建 Proxy 时构造方法传入的 IBinder 也就是客户端绑定时得到的 Binder 类对象,通过系统进程调用服务端的 onTransact 方法,执行也就切换到了服务端,服务端也就拿到了序列化后的参数,返回值以及代表哪个方法的 int 值。transact 方法中会把存储参数和返回值的可序列化数据传递到 onTransact 方法中

onTransact 方法中会将序列化的参数反序列化,再根据代表客户端调用哪个方法的 int 值,将参数传入相应方法得到返回值。 这里需要注意,传入相应的方法,其实是调用的 Stub 类的子类的方法,也就是服务端实现了 Stub 类中抽象方法的那个类的对象的方法,这样最终任务的执行是在 Stub 中执行的,并不是在 Proxy 类中。 再将返回值进行序列化后写入参数中传来的输出型 Parcel 中,这时候,注意,服务端就执行结束了。

服务端执行结束之后,系统进程会返回一个 Boolean 值表示是否成功调用,系统进程收到这个返回值时后将该返回值和及 将输出型的 Pacel 返回到客户端进程,客户端进程的 Binder 线程会被唤醒,客户端线程唤醒后,会根据是否执行成功的返回值接收执行结果

注:

  1. 客户端发起请求时当前线程就会挂起,所以要是执行任务耗时则需要客户端在子线程中调用

  2. 服务端相应请求执行过程在 Binder 的线程池中,即已经在子线程中了,所有 Binder 中执行过程无需再开启子线程

三、Binder 的死亡代理

通过 linkToDeath 和 unlinkToDeath 方法可以在客户端为 Binder 绑定和解绑死亡代理
死亡代理是一个 DeathRecipient 类,内部又一个 binderDied 方法,我们需要实现这个方法,在 Binder 死亡的时候,系统就会回调 binderDied 方法,我们就可以移除之前绑定的 binder 代理并重新绑定完成服务。

四、总结

  • Worker:需要传递的对象需要实现的接口,继承 IInterface 接口

  • Stub:服务器端需要实现的对象的父类继承 Binder 类

  • Proxy:代理类,客户端调用服务器端对象时使用的类

当 Stub 的子类实例化时,Binder 中会存起来,并将该 Binder 返回到客户端,客户端拿到这个 Binder 会根据是否是同进程操作,不同进程时会通过 Binder 构造

4.1 AIDL原理

Binder 在 AIDL 中应用,服务器端将客户端需要的对象转换成一个 Binder 对象

AIDL 原理,即客户端需要一个服务器端的对象,客户端和服务器端不在同一进程,服务端返回一个 Binder 对象给客户端,客户端根据将 Binder 对象转换成需要的对象的 Proxy 代理,需要调用服务端执行任务时就调用该 Proxy 代理的方法,Proxy 代理的方法执行时会通过 Native 层通过系统进程找到其他进程中匹配的 Stub 子类,并调用其执行任务方法,从而实现客户端和服务端的进程间通信。

4.2 AIDL 工作过程

首先,我们服务器端有一个可以执行任务的对象需要传递到客户端,然而不同进程之间不可以直接调用对象的方法,从而通过 Binder 来实现进程间的通信。

  1. aidl文件 定义 .aidl 类型文件,其中声明要实现的功能方法

  2. 定义接口 首先,定义 AIDL 类型的接口,也就是需要传递的对象类型接口,继承 IInterface ,并在其中根据 aidl 文件的内容定义了这个类的可以实现功能的抽象方法

  3. 接口中定义内部类 Stub 在接口中定义 Stub 类,该类实现了我们需要的接口,并继承了 Binder

  4. 服务端实现接口 服务端要定义类实现 Stub 类,然后完成我们自己定义的接口的抽象方法

  5. Stub 类的实现 Stub 类中定义了 asInterface 可以返回一个我们需要的类对象,如果是同进程则返回本身,如果是非同进程则返回 Stub 的内部类 Proxy 代理类对象

  6. 客户端请求 客户端绑定时服务器返回服务端实现的接口类型的对象,客户端得到的 Binder 对象就是 Stub 对象,客户端调用 Stub 对象的 asInterface 方法得到需要的类型对象

  7. 同进程对象工作 客户端得到需要的对象后,调用其方法,如果是同进程,参数可以直接传递,同时 asInterface 得到的就是服务端实现的 Stub 对象,可以直接执行方法返回结果

  8. 跨进程对象工作 如果是跨进程时,asInterface 得到的就是 Stub 的内部类 Proxy 类的对象,该对象继承了我们需要的接口,并实现了其抽象方法,Proxy 的构造需要一个 Stub 类对象

  9. Proxy 的工作过程 其实现的抽象方法执行时,会先将方法参数序列化,再调用 Stub 方法的 transact 方法将序列化后的参数,方法 int 类型的标识传入,线程挂起,服务端在 Binder 线程中执行任务后将序列化后的结果返回。Proxy 的工作方法中,得到结果后,将序列化后的结果反序列化成方法的返回值类型,返回。

  10. transact 方法 transact 方法最后会调用 Stub 的 onTransact 方法

  11. Stub 的 onTransact 方法 onTransact 方法执行在服务器进程的 Binder 线程池中,根据方法标识,将序列化后的参数反序列化,在服务端执行,得到返回值后序列化,最后将结果返回,传回到客户端。客户端处理后完成。

4.3 Binder 机制在系统中的应用

Android 系统启动之后会启动很多的 Service,例如 ActivityManagerService 等,在开发中我们可能需要调用这些 Service 的方法,但是由于是不同进程是不可以直接调用的,这时候我们在 ServiceManager 中通过 Binder 机制跨进程拿到 AMS 的代理 ActivityManagerProxy,通过代理即可实现调用 AMS 的方法。

上面提到了 ServiceManager,那 ServiceManager 又是怎么工作的呢,ServiceManager 其实是 ServiceManagerProxy 的代理,ServiceManagerProxy 是 ServiceManagerNative 的代理,ServiceManagerNative 继承了 IServiceManager 并运行在系统进程 ,我们看出来了,原来 ServiceManager 也是通过 Binder 实现跨进程的

我们在应用中使用 ServiceManager 在获取系统服务时,如果 ServiceManager 代理的 ServiceManagerNative 没有初始化,则会通过 Natice 层来通过 Binder 机制完成 ServiceManagerNative 初始化,此时的服务端是系统进程,之后我们就可以使用 ServiceManager 来协助我们获取其他的系统服务了。ServiceManager 在这里实现了 Binder 连接池的作用。同一个一个池对象,我们可以获得多个 Binder 对象。