AIDL机制

前言

前一段时间,除了忙在公司项目的重构和优化,然后就主要把时间花在 Java 的一些基础进行回顾。按照我的计划,接下来的一段时间里面,我会把时间主要花在 Android 的 Framework 层,当然也计划了 Kotlin 的协程呀、Jetpack 组件呀这些的深入理解….

# 目录结构

# AIDL

IMyAidlInterface.aidl 文件内容:

Word.aidl 文件内容:

Word.java 文件内容:

其中,在 IMyAidlInterface.aidl 文件中,允许通过一个或多个方法来声明接口:

  • AIDL 默认支持 Java 的八种基本类型;

  • AIDL 还支持 StringCharSequenceListMap 类型,但在 ListMap 中的元素必须是八种基本类型、或 AIDL 生成的接口、或 Parcelable 类型;

  • List 支持泛型,而 Map 不支持泛型;

  • 如果 AIDL 使用到自定义的 Parcelable 对象,那么需要新建一个和需要使用到的 AIDL 文件同名、并位于同一个包内,同时需要通过 import 关键字导入到需要使用的 AIDL 文件中;

  • AIDL 除了使用到的基本类型,其他类型都需要标上方向:inoutinout

新建好上面的三个文件,随后通过 Android Studio 可以编译生成一个 IMyAidlInterface.java 文件(文件里面的内容过多,我在这里就不详细引入了)。然后依次编写 AIDL 的 Client 端和 Server 端,如下:

AIDLService.java

AndroidManifest.xml

MainActivity.java

MainActivityAIDLService 中编写的代码都是拿到 Binder 引用,进行 IPC 的跨进程通信。其中在 AIDLService 中,是通过:

new IWordManager.Stub() 的创建对象的方式,拿到 Binder 引用,然后把该引用通过 onBind() 方法返回。其中,Stub 是一个接口,并且继承 android.os.Binder 和实现 com.example.aidl.IWordManager, 然后把该 Binder 对象(也可以看作 IWordManager)的引用传到 Client 端,即 MainActivity

上面的 val iWordManager = IWordManager.Stub.asInterface(service) 拿到了 Server 端传过来的 Binder 对象,然后转换成 IWordManager, 最后在 AIDLServiceMainActivity 都持有了 IWordManager 的引用,也就意味着可以调用 IWordManager 里面的方法。

为什么会在 AIDLServiceMainActivity 都可以看作持有 IWordManager 的引用呢,因为 AIDL 也是一个 C/S 模型的通信模式,Client 端和 Server 端都是相对而言的。在 AIDLService 请求 MainActivity 的内容的时候,AIDLService 可以看作 Server 端,当 MainActivity 接收 MainActivity 内容的时候,AIDLService 可以看作 Client 端。

那么在 AIDLService 中是如何把 IWordManager 对象(也就是 Binder)引用传到 MainActivity 中的呢?我们可以通过分析源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//AIDLService.java
private Binder mBind = new IWordManager.Stub() { // 1

@Override
public List<Word> getWord() throws RemoteException {
return mWordList;
}

@Override
public void addWord(Word word) throws RemoteException {
Log.d(TAG, "AIDLService ---- >> addWord: " + word.somethingWord);
mWordList.add(word);
}
};

我们通过查看注释 1 的代码:

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
// IWordManager 源码

public static abstract class Stub extends android.os.Binder implements com.example.aidl.IWordManager {
private static final java.lang.String DESCRIPTOR = "com.example.aidl.IWordManager";

// 构造方法,
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

// 用于把 Binder 对象转换成 IWordManager 对象
public static com.example.aidl.IWordManager asInterface(android.os.IBinder obj) {
...
// 查询进程本地是否存在 IWordManager 对象,存在则直接返回
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidl.IWordManager))) {
return ((com.example.aidl.IWordManager) iin);
}
// 通过跨进程获取 IWordManager 对象, 通过查看下面的注释 2
return new com.example.aidl.IWordManager.Stub.Proxy(obj);
}

...

// 2
// Proxy 是被另外的一个进程的 IWordManager 所初始化,所以返回的是另外一个进程的 IWordManager 引用
private static class Proxy implements com.example.aidl.IWordManager {
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 java.util.List<com.example.aidl.Word> getWord() throws android.os.RemoteException {
// 需要发送的数据 _data (android.os.Parcel 对象可以通过 Binder 发送)
android.os.Parcel _data = android.os.Parcel.obtain();
// 接收的数据 _reply
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.aidl.Word> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 成功返回 true,失败返回 false
boolean _status = mRemote.transact(Stub.TRANSACTION_getWord, _data, _reply, 0); // 3
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getWord();
}
_reply.readException();
// 通过调用 android.os.Parcel#createTypedArrayList() 方法,读取 _reply 里面的数据
_result = _reply.createTypedArrayList(com.example.aidl.Word.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

@Override
public void addWord(com.example.aidl.Word word) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((word != null)) {
_data.writeInt(1);
word.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addWord, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addWord(word);
return;
}
_reply.readException();
} finally {
....
}
}

...
}

static final int TRANSACTION_getWord = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addWord = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

....
}

在继续往下阅读源码的时候,需要明白以下几个知识点,不然容易会看得云里雾里:

  • 关于 _data_reply 两个 android.os.Parcel 对象, _data 主要是用来存放 Client 端需要发送的数据,而 _reply 主要是用来存放用来接收的数据。对于 android.os.Parcel 是一个可以通过 Binder 传输的数据结构,简单来说,Binder 之间的通信交流数据是以 android.os.Parcel 作为载体,而数据通过序列化后存在载体 android.os.Parcel 上(这里需要注意的是,parcelParcelable 是两个不同的概念,而 ParcelableSerializable 是两个类似的概念)。

  • Binder#transact() 是客户端和服务端的核心方法,通过调用该方法,客户端会暂时挂起,等待服务端的数据写入 _reply 返回,对于 Binder#transact() 的四个方法参数(code、data、reply、flags)需要以下的几点说明:

    • code 指的是代表 AIDL 接口文件中定义的方法的 ID,因为在客户端和服务端中的 AIDL 文件中,定义了一一对应的方法。因而在 AIDL 文件转成 .java 文件的时候,系统会为每个方法生成对应的 ID,通过阅读源码,可以知道方法的 ID 是从 1 开始的;

    • datareply 对应上文的 _data_reply;

    • 第四个参数 flags 当为 0 的时候,表示全双工的 RPC 机制,也就是 data 发送数据,而 reply 接收数据;当为 1 的时候,表示半双工的 RPC 机制,也就是 data 发送数据,而 reply 是没有数据的。

通过阅读 IWordManager.java#Proxy 源码,我们发现, AIDL 是通过:

1
2
3
boolean _status = mRemote.transact(Stub.TRANSACTION_getWord, _data, _reply, 0);
...
boolean _status = mRemote.transact(Stub.TRANSACTION_addWord, _data, _reply, 0);

到此,我们可以得出结论:

  • _data 中存入客户端需要发送的数据;

  • 通过 mRemote#transact()_data 传给服务端;

  • 通过接收 _reply 接收数据,从中取出服务端发送过来的数据;

上述两个语句通过 mRemote 去发起调用 transact() 方法的,同时我们通过查看代码,知道 mRemote 就是 Binder 对象。通过继续阅读 Binder#transact() 方法,可以发现其最终是调用 Binder#onTransact(int, Parcel, Parcel,int) 方法,因为在 IWordManager#Stub 是继续了 Binder, 以此调用的是 Stub#onTransact() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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: {
....
return true;
}
// getWord()
case TRANSACTION_getWord: {
....
return true;
}
// addWord()
case TRANSACTION_addWord: {
....
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}

上面的代码可以看出,当接收到 onTransact() 的调用后,直接来个 swith 语句进行 case,这里我以 getWord() 方法为例,分析 _reply 是如何从中拿出服务端的数据的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
....
switch (code) {
....
case TRANSACTION_getWord: {
data.enforceInterface(descriptor);
// _result 为 getWord() 的返回值
java.util.List<com.example.aidl.Word> _result = this.getWord();
reply.writeNoException();
// 从 reply 中取出数据写入 _result 中
reply.writeTypedList(_result);
return true;
}
....
}
}

可以发现,在 Proxy#transact() 发起调用时候,在 Stub#onTransact() 中响应调用,看起来好像就是在本地 Proxy#transact() “直接”调用了 Stub#onTransact()。其实这个 “直接” 的过程是 Binder 为我们封装了细节,让我们感受不到其中的通信的曲折,我想这也是 Binder 跨进程通信最初设计的目的吧。

小结

现在我们已经完成了 Wordmanager.java 的分析,下面我附上两张图片来做一个小总结:

这篇分析 AIDL 机制是基于 Binder 的应用层面的分析,如果我们需要深入了解 Binder 的跨进程原理,我们仍然需要深入到 Binder 的 Framework 层和 Native 层等进行源码的分析。

本文标题:AIDL机制

文章作者:

发布时间:2020年04月21日 - 12:04

最后更新:2021年06月20日 - 19:06

原始链接:https://hndroid.github.io/2020/04/21/AIDL%E6%9C%BA%E5%88%B6/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。