0%

理解Android的AIDL

前言

AIDL是Android提供的方便应用层进行进程间通信的描述语言,同时也提供了AIDL语言转成Java语言的工具,方便客户端开发,下面我们通过一个简单的例子来说明AIDL是怎么使用的,以及这样设计背后的意义。

DEMO

定义aidl

1
2
3
4
interface IBookManager {
List<Book> getBookList();
void addBook(Book book);
}

针对这样一个AIDL文件,编译之后,生成一个IBookManager.java文件,代码如下

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
102
103
104
105
106
107
108
109
110
111
// Declare any non-default types here with import statements
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.nancyyihao.aidlserver.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.nancyyihao.aidlserver.IBookManager"; // Binder Indentifier
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.nancyyihao.aidlserver.IBookManager interface,
* generating a proxy if needed.
*/
public static com.nancyyihao.aidlserver.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.nancyyihao.aidlserver.IBookManager))) {
return ((com.nancyyihao.aidlserver.IBookManager) iin); // local Binder
}
return new com.nancyyihao.aidlserver.IBookManager.Stub.Proxy(obj); // remote Binder
}

public android.os.IBinder asBinder() {
return this;
}

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_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.nancyyihao.aidlserver.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.nancyyihao.aidlserver.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.nancyyihao.aidlserver.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.nancyyihao.aidlserver.IBookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}

public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

public java.util.List<com.nancyyihao.aidlserver.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.nancyyihao.aidlserver.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.nancyyihao.aidlserver.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

public void addBook(com.nancyyihao.aidlserver.Book book) 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 ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.nancyyihao.aidlserver.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.nancyyihao.aidlserver.Book book) throws android.os.RemoteException;
}

代码分析

生成的代码主要有三部分,如下图所示

  • 第一部分:IBookManager的Java接口,其实就是把AIDL语言转成了Java语言
  • 第二部分:Stub抽象类,留给服务端实现,主要包含两个方法,一个是asInterface方法,该方法会返回一个实现了IBookManager的一个实例;另外一个onTransact方法,该方法会根据不同的transact code调用执行相应的业务逻辑,这个业务逻辑由Stub子类去实现
  • 第三部分:Proxy类,工作在客户端侧,实现了对Binder数据请求的封装,会调用mRemote.transact方法进行binder请求。

image

对于服务端,只需要写一个类实现具体的业务了逻辑就可以了;比如BookManagerService,而对于客户端

1
2
IBookManager manager = IBookManager.Stub.asInterface(IBinder);
manager.getBookList();

也只需要调用Stub.asInterface()拿到相应的实例,然后就直接调用相应的方法即可。这样设计的好处首先是完全屏蔽了底层的Binder通信,IPC就相当于一个普通的方法调用,其次是服务端也不用关心数据接收和组装(当然服务端要做好同步),只需要实现好对应的业务逻辑,不得不说这样的设计思路值得我们学习。

通过上面的例子我们也可以知道,想要使用Binder通信,当然也可以不用AIDL,完全可以自己写对应的Stub/Proxy/IBookManager接口类,核心是要根据接口封装出对应的数据接口,然后调用mRemote.transact方法就可以和服务端进行通信,不过既然有工具帮你做好,干嘛不用呢?

总结

通过查看Android源码之后,我们可以看到Framework层绝大部分系统服务都遵循这样的设计模式,先定义一个接口(服务),然后实现对应的Stub/Proxy/Service(Service驻留在system_server进程,Proxy都是给app侧使用的),最后客户端就可以通过ServiceManager.getService拿到系统提供的服务,IPC就相当于一个简单方法调用,下一篇文章我们会以ActivityManager为例,分析系统调用过程,敬请期待。