Android 进程间通信工具 AIDL

在使用Binder进行进程间通信,服务端和客户端进行数据传递时,数据的传递(请求、响应)需要将数据打包成Parcel数据包,Parcel数据包写入和读取都需要按相同的顺序,给编码工作造成额外的工作量。而AIDL作为一个工具,可以将符合规范的aidl文件转换为java文件,将数据打包,解包成Parcel的工作给我们实现了,让我们专注于核心业务。

aidl文件

1
2
3
4
5
6
7
8
9
10
// IAidlEx.aidl
package com.ccflying;
// Declare any non-default types here with import statements
interface IAidlEx {
int getAge(String name);
boolean setNameAndAge(String name, int age);
}

上面的aidl文件中,定义了两个接口,编译器会自动生成IAidlEx.java文件。

IAidlEx.java

1
2
3
4
5
6
7
8
9
10
11
12
public interface IAidlEx extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ccflying.IAidlEx {
private static class Proxy implements com.ccflying.IAidlEx {
}
// ...
}
// ...
}

编译工具生成的IAidlEx文件中,定义了三个类,分别是:

  • IAidlEx: 接口定义,该类里面包含了aidl文件中定义的两个方法。
  • IAidlEx.Stub: 内部类Stub,继承自Binder,并且实现IAidlEx接口,具体业务实现,由Stub子类实现,服务端需要继承此类,实现接口中定义的方法。
  • IAidlEx.Stub.Proxy: 内部类Stub.Proxy,在客户端中使用,用于向服务端发起请求,主要工作是将数据打包,调用mRemote.transact发起请求,获取服务端的响应结果。

IAidlEx

1
2
3
4
5
public interface IAidlEx extends android.os.IInterface
{
public int getAge(java.lang.String name) throws android.os.RemoteException;
public boolean setNameAndAge(java.lang.String name, int age) throws android.os.RemoteException;
}

IAidlEx.Stub

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
public static abstract class Stub extends android.os.Binder implements com.ccflying.IAidlEx {
private static final java.lang.String DESCRIPTOR = "com.ccflying.IAidlEx";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ccflying.IAidlEx interface,
* generating a proxy if needed.
*/
public static com.ccflying.IAidlEx asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ccflying.IAidlEx))) {
return ((com.ccflying.IAidlEx)iin);
}
return new com.ccflying.IAidlEx.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_getAge:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.getAge(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_setNameAndAge:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
boolean _result = this.setNameAndAge(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(((_result)?(1):(0)));
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
static final int TRANSACTION_getAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setNameAndAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}

onTransact方法中,根据请求标识,解包数据包 -> 调用业务方法 -> 打包数据包返回

IAidlEx.Stub.Proxy

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
private static class Proxy implements com.ccflying.IAidlEx {
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 getAge(java.lang.String name) 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(name);
mRemote.transact(Stub.TRANSACTION_getAge, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public boolean setNameAndAge(java.lang.String name, int age) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeInt(age);
mRemote.transact(Stub.TRANSACTION_setNameAndAge, _data, _reply, 0);
_reply.readException();
_result = (0!=_reply.readInt());
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

实现了IAidlEx接口,调用mRemote.transact向远程服务端发起请求。

实现Service服务

继承IAidlEx.Stub类,实现远程服务Binder业务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class AidlExServiceImpl extends IAidlEx.Stub {
final String TAG = "AidlExServiceImpl";
private HashMap<String, Integer> users;
public AidlExServiceImpl() {
users = new HashMap<>();
}
@Override
public int getAge(String name) throws RemoteException {
Log.e(TAG, "getAge: " + name);
Integer it = users.get(name);
if (null == it)
return -1;
return it.intValue();
}
@Override
public boolean setNameAndAge(String name, int age) throws RemoteException {
Log.e(TAG, "setNameAndAge: " + name + ", " + age);
users.put(name, age);
return true;
}
}

继承Service类,实现远程服务AidlExService

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
public class AidlExService extends Service {
private final String TAG = "AidlExService";
private AidlExServiceImpl serviceImpl;
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: ");
serviceImpl = new AidlExServiceImpl();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: " + serviceImpl.hashCode());
return serviceImpl;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
}

发布服务

1
2
<service android:name="com.ccflying.AidlExService"
android:process=":remote" />

跨进程调用Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// service : android.os.BinderProxy@6fb8e95
mAidlExService = IAidlEx.Stub.asInterface(service);
// mAidlExService: com.ccflying.IAidlEx$Stub$Proxy
}
@Override
public void onServiceDisconnected(ComponentName name) {
conn = null;
}
};
bindService(new Intent(this, AidlExService.class), conn, Service.BIND_AUTO_CREATE);
// 请求setNameAndAge
boolean rs = mAidlExService.setNameAndAge("ccf", 27);
// 请求getAge
int age = mAidlExService.getAge("ccf");