Android Studio AIDL 实战

AIDL是Android Interface Definition Language 的缩写,即Android 接口定义语言。那么AIDL到底是干什么用的呢?简单的说AIDL就是用来进行进程间通信的 ,可是Android已经有了Messenger、ContentProvider、Socket等进程间通信方式,那么我们为什么还需要AIDL呢?以及我们什么时候应该使用AIDL呢? 其实这 两个问题的答案可以在Android官方文档中找到,在Android的官方文档中有下面的描述:

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

只有需要多个不同App通过IPC访问服务而且还需要在服务中处理多线程时才应该使用AIDL。从这里我们应该明白了为什么要使用AIDL,以及什么时候使用AIDL。

AIDL实战

简单的了解了AIDL之后,接下来我们将通过一个简单的示例演示一下AIDL的使用。

新建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
//Book.java 
package com.weiqianghu.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
* Created by huweiqiang on 2016/8/1.
*/

public class Book implements Parcelable{
public int bookId;
public String bookName;

protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}

public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}

@Override
public Book[] newArray(int size) {
return new Book[size];
}
};

public int getBookId() {
return bookId;
}

public void setBookId(int bookId) {
this.bookId = bookId;
}

public String getBookName() {
return bookName;
}

public void setBookName(String bookName) {
this.bookName = bookName;
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
}

//Book.aidl
package com.weiqianghu.aidl;

parcelable Book;

//IBookManager

// IBookManager.aidl
package com.weiqianghu.aidl;

import com.weiqianghu.aidl.Book;

interface IBookManager {

List<Book> getBookList();

void addBook(in Book book);
}

什么三个文件中,Book.java 是一个Java类,它实现了Parcelable接口,Book.aidl是Book类在AIDL中的声明,IBookManager 是我们定义的一个接口,里面有两个方法:getBookList和addBook。
注意:

* 无论自定义的Parcelable对象和AIDL对象和当前的AIDL文件是否在同一个包中,都要显式import进来
* 如果AIDL使用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件
* AIDL中除了基本数据类型,其他类型的参数都必须标上方向:in、out、inout。

远程服务端Service的实现

首先新建如下Service

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
package com.weiqianghu.aidl;

public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";

private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();


@Override
public void onCreate() {
super.onCreate();
mBooks.add(new Book(1, "Android 群英传"));
mBooks.add(new Book(2, "Android 开发艺术探索"));
}

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

private Binder mBinder = new IBookManager.Stub() {

@Override
public List<Book> getBookList() throws RemoteException {
return mBooks;
}

@Override
public void addBook(Book book) throws RemoteException {
mBooks.add(book);
}
};
}

<service
android:name=".BookManagerService"
android:enabled="true"
android:exported="true"
android:process=".aidl.BookManagerService">
</service>

上面新建了一个服务端Service,在onBind中返回了Binder对象,使用 CopyOnWriteArrayList 进行线程同步。在Manifest文件中将Service置于单独进程中。

客户端实现

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
package com.weiqianghu.aidl;

public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}

private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);

try {
List<Book> list = bookManager.getBookList();

Log.i(TAG, "onServiceConnected: " + list.toString());

bookManager.addBook(new Book(3, "Android 源码设计模式分析与实战"));

list = bookManager.getBookList();
Log.i(TAG, "onServiceConnected: " + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName componentName) {

}
};
}

以上就是客户端代码,运行程序Log如下:

08-01 15:52:38.166 21955-21955/com.weiqianghu.aidl I/MainActivity: onServiceConnected: [1,Android 群英传, 2,Android 开发艺术探索]
08-01 15:52:38.170 21955-21955/com.weiqianghu.aidl I/MainActivity: onServiceConnected: [1,Android 群英传, 2,Android 开发艺术探索,3,Android 源码设计模式分析与实战]

可以看到客户端成功调用了服务端的方法。

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\Android-workplace\\AIDL\\app\\src\\main\\aidl\\com\\weiqianghu\\aidl\\IBookManager.aidl
*/

package com.weiqianghu.aidl;

public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/

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

/**
* Construct the stub at attach it to the interface.
*/

public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.weiqianghu.aidl.IBookManager interface,
* generating a proxy if needed.
*/

public static com.weiqianghu.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.weiqianghu.aidl.IBookManager))) {
return ((com.weiqianghu.aidl.IBookManager) iin);
}
return new com.weiqianghu.aidl.IBookManager.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_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.weiqianghu.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.weiqianghu.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.weiqianghu.aidl.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.weiqianghu.aidl.IBookManager {
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.weiqianghu.aidl.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.weiqianghu.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.weiqianghu.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

@Override
public void addBook(com.weiqianghu.aidl.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.weiqianghu.aidl.Book> getBookList() throws android.os.RemoteException;

public void addBook(com.weiqianghu.aidl.Book book) throws android.os.RemoteException;
}

在gen目录下可以看到系统根据IBookManager.aidl生成了IBookManager.java这个类。IBookManager 继承了 IInterface 这个接口,所有需要在Binder 中传输的接口都需要继承IInterface接口。通过查看 IBookManager.java 源码我们可以看到,首先它声明了两个方法getBookList和addBook ,这两个方法就是我们在IBookManager.aidl 定义的方法。接着它声明了内部类Stub,Stub 继承了Binder,并且实现了 IBookManager 接口。可以看到首先Stub中声明了两个int型id分别用来表示getBookList方法和addBook方法。其次Stub中还有两个方法分别是: asBinder 和 asInterface 方法,asInterface 用于将服务端的Binder对象转换为客户端所需的AIDL接口类型的对象,asBinder 方法用于返回当前的Binder对象。最重要的是 Stub 中又定义了内部代理类 Proxy ,Proxy也实现了 IBookManager 接口,这也就是设计模式中的代理模式的应用。 Proxy 类中主要实现了getBookList和 addBook 方法。这就是AIDL的实现原理。

总结

Binder是Android系统中一个非常重要的跨进程通信机制,Binder 也是非常不容易理解的一个话题,因此本文并没有深入 Binder 细节,而是从AIDL入手介绍了 Binder 机制,简答来说 Binder 是 Android 的一个类,它实现了 IBinder接口, Binder 是 ServiceManager 连接各种 Manager 和相应 ManagerService 的桥梁, Binder也主要用在 Service 中。

参考资料:
Android 开发艺术探索 第二章 IPC 机制相关章节