Earth Guardian

You are not LATE!You are not EARLY!

0%

AIDL 详解

概念

全称:AIDL: Android Interface Definition Language ,它是 Android 接口定义语言。用它定义客户端与服务使用进程间通信 ( IPC ) 进行相互通信时都认可的编程接口。Android 中一个进程通常无法访问另一个进程的内存,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。

AIDL 接口的调用是直接函数调用(同步的),执行进程取决于调用来自本地进程还是远程进程中的线程:

  • 来自本地进程的调用在发起调用的同一线程内执行
    如果该线程是 UI 主线程,则该线程继续在 AIDL 接口中执行;如果该线程是其他线程,则其在其他中线程执行。但是这种情况根本不应该使用 AIDL,而是应该通过实现本地 Binder 类创建接口
  • 来自远程进程的调用,平台分派给当前应用自有进程内部维护的线程池
    必须为来自未知线程的多次并发传入调用做好准备,即 AIDL 接口的实现必须是完全线程安全实现。

AIDL 接口首次发布后对其进行的任何更改都必须保持向后兼容性,以避免中断其他应用对服务的使用。因为必须将 .aidl 文件复制到其他应用,才能让这些应用访问服务的接口,因此必须保留对原始接口的支持。

注意:如下介绍都是针对跨进程 AIDL 的用法。如果是在相同进程中,AIDL 相当于直接调用,和本地扩展 Binder 用法一样,具体原因参看 asInterface 的返回值。

语法格式

每个 .aidl 文件都必须定义单个接口,并且需要包含接口声明和方法,不支持定义字段。

支持的数据类型

  • 基本数据类型
    byte, int , short, long, boolean, char, float, double, String, CharSequence 等。
  • 集合类型
    List 中的数据类型必须是基本类型或者 Parcelable 类型。可选择将 List 用作“通用”类(例如,List<String>)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
    Map 支持的数据类型和 List 相同,但是不支持通用 Map 类(即不支持 Map<String,Integer> 这种格式)。另一端实际接收的具体类始终是 HashMap ,但生成的方法使用的只能是 Map 接口。
    示例: void ListAndMapTypes(inout List<String> list, inout Map map);
  • 自定义 Parcelable 类型
    必须为这些类型加入一个 import 语句,即使这些类型是和接口相同的软件包中定义。

数据走向标记

AIDL 进程传递数据时,都有一个数据拆包和打包的过程,这是一个很耗系统内存的。**AIDL 作为参数不需要指定,基本数据类型(int, Long, String...)默认且只能为 in,所有其他非基本数据类型都需要指定数据走向的方向标记**。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface IMyIPCAidlInterface {
// 1. AIDL 作为参数,不需要指定
void registerCallback(IMyAidlInterfaceCallback cb);
void unregisterCallback(IMyAidlInterfaceCallback cb);

// 2. 基本数据类型,默认且只能为 in
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);

// 3. 非基本数据类型,必须指定具体的方向
void testListAndMapTypes(inout List<String> list, inout Map map);
MyDataParcelable updateBookOut(out MyDataParcelable myDataParcelable);
...
}

举例客户端数据传递到服务端,走向标记解析:

  • in
    类似值传递,形参只拷贝了数据内容到服务器端,服务器端的修改不会影响到客户端的实参。
  • out

传递空对象,但是服务端的修改会同步写入客户端实参,相当于只传递了参数类型。形参传递时服务器端并没有直接使用,而是重新 new 了一个空对象;客户端的参数是无法传递进服务端的,在服务端对空对象填充完数据后,会同步写入客户端的实参,即服务端的修改会同步到客户端。

  • inout
    类似引用传递,形参拷贝了数据内容到服务器端,但是服务器端的修改会重新写回客户端,所以客户端的实参跟着被修改了,也就是服务端的修改会同步影响客户端。

根据后文的源码分析,非基本数据类型不管是哪种走向标记,客户端和服务端在跨进程通信时,对象都不是直接传递的,而是在 Studb.Proxy 中通过 Parcel 重建了数据做中转,中转的过程中模拟了值传递或者引用传递的效果。示例:

注意:走向标记仅仅在跨进程时才有上面的含义。如果都是在相同进程中执行,不管如何标记都是引用传递,即相当于直接调用,并没有使用代理,具体参考 asInterface 的返回值。

oneway 关键字

表示用户请求相应功能时不需要等待响应可直接调用返回,非阻塞效果。该关键字可以用来声明接口或者声明方法,如果接口声明中用到了 oneway 关键字,则该接口声明的所有方法都采用 oneway 方式 。通过这种方式,服务端在回调客户端的方法时,可以同时通知所有客户端,而不必等待客户端执行完毕。

通俗来讲oneway 修饰方法 F,在包含了 F 的这段程序会先执行完了,才执行 F 方法。如果没有 oneway 关键字定义,即正常流程,程序先等待 F 方法调用完才会继续执行。参看代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Log.d(TAG, "handleMessage: MSG_UPDATE_DATA");
MyDataParcelable data = RandomData.genMyDataParcelable();
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).updateData(data);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
Log.d(TAG, "handleMessage: MSG_UPDATE_DATA, Callback Done!");

有没有 oneway 关键字定义的前后对比 Log

1
2
3
4
5
6
7
8
9
10
11
12
13
// oneway 关键字定义 updateData,不会阻塞原有程序 handleMessage,执行完后才调用 updateData
// 服务端可以同时通知所有的客户端,不必等待客户端执行完毕
11:58:10.236 18798-18798/*:aidl D/:MyAIDLService:: handleMessage: MSG_UPDATE_DATA
11:58:10.236 18798-18798/*:aidl D/:MyAIDLService:: handleMessage: MSG_UPDATE_DATA, Callback Done!
11:58:10.237 18706-18726/* D/:ShowIPCActivity:: updateData:
11:58:13.237 18706-18726/* D/:ShowIPCActivity:: updateData: done!

// 没有 oneway,所有执行按照正常流程,直到 updateData 执行完才继续向前
11:59:44.920 19822-19822/*:aidl D/:MyAIDLService:: handleMessage: MSG_UPDATE_DATA
11:59:44.921 19713-19734/* D/:ShowIPCActivity:: updateData:
11:59:47.921 19713-19734/* D/:ShowIPCActivity:: updateData: done!
11:59:47.922 19822-19822/*:aidl D/:MyAIDLService:: handleMessage: MSG_UPDATE_DATA, Callback Done!

oneway 关键字最终是在 Binder 机制中解析的,具体为 IPCThreadState.cpp::transact 中判断是否阻塞执行,具体参考Android Binder 机制

自定义 Parcelable 数据在 IPC 中传递

实现 Parcelable 自定义类

在做进程间通信时,自定义的 Parcelabel 类除了完成默认的实现,还需要多增加两个方法(具体需求可以参见后面的源码分析):

  • 空的构造函数
    out 走向时,需要重新 new 一个空对象,就是调用的该构造函数:
1
2
3
public MyDataParcelable(){

}
  • Parcel 中读取数据
    Parcelable 数据 clone 时,需要通过该方法中转:
1
2
3
public void readFromParcel(Parcel in){
...
}

创建同名 AIDL 文件申明自己

凡是自定义的 Parcelabel 类,在 AIDL 中使用时,必须要先定义自己,并用 parcelable 关键字做声明:

1
2
3
4
5
6
7
// MyDataParcelable.aidl
// 1. 包名
package ***.ipc;
// 2. parcelable 关键字做声明
// Declare MyDataParcelable so AIDL can find it and knows that
// it implements the parcelable protocol.
parcelable MyDataParcelable;

如果不定义,在使用过程中编译时会抛出异常:
couldn't find import for class ***.ipc.MyDataParcelable

AIDL 实现双向通信及示例

服务端创建 AIDL 文件

服务端创建 IMyIPCAidlInterface.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
// IMyIPCAidlInterface.aidl
package com.ymzs.androidbasicknowledge.ipc;

// Declare any non-default types here with import statements
import com.ymzs.androidbasicknowledge.ipc.MyDataParcelable;
import com.ymzs.androidbasicknowledge.ipc.IMyAidlInterfaceCallback;

interface IMyIPCAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);

void testListAndMapTypes(inout List<String> list, inout Map map);

MyDataParcelable updateBookIn(in MyDataParcelable myDataParcelable);
MyDataParcelable updateBookOut(out MyDataParcelable myDataParcelable);
MyDataParcelable updateBookInOut(inout MyDataParcelable myDataParcelable);

void registerCallback(IMyAidlInterfaceCallback cb);
void unregisterCallback(IMyAidlInterfaceCallback cb);
}

服务端

  • 创建 IMyIPCAidlInterface.Stub 实例,并实现接口函数
    private IMyIPCAidlInterface.Stub mBinder = new IMyIPCAidlInterface.Stub() {...}
  • onBind 返回该实例
    返回 IMyIPCAidlInterface.Stub 的实例:
1
2
3
4
5
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return mBinder;
}
  • 创建回调列表以及注册和取消
1
2
3
4
5
private final RemoteCallbackList<IMyAidlInterfaceCallback> mCallbacks
= new RemoteCallbackList<IMyAidlInterfaceCallback>();
...
if(cb != null) mCallbacks.register(cb);
if(cb != null) mCallbacks.unregister(cb);
  • 回调客户端方法
    这里可以通过 oneway 关键字定义回调是否阻塞。
1
2
3
4
5
6
7
8
9
10
11
12
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
// 逐个回调客户端
mCallbacks.getBroadcastItem(i).updateData(data);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();

客户端创建 AIDL 文件用于回调

客户端创建 IMyIPCAidlInterfaceCallback.aidl 文件,供服务端调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// IMyAidlInterfaceCallback.aidl
package com.ymzs.androidbasicknowledge.ipc;

// Declare any non-default types here with import statements
import com.ymzs.androidbasicknowledge.ipc.MyDataParcelable;

/**
* Example of a callback interface used by IMyAidlInterfaceCallback to send
* synchronous notifications back to its clients. Note that this is a
* one-way interface so the server does not block waiting for the client.
*/
oneway interface IMyAidlInterfaceCallback {
void updateData(in MyDataParcelable myDataParcelable);
}

客户端

  • 创建 IMyIPCAidlInterface 实例
    private IMyIPCAidlInterface mAIDLService = null;

  • 绑定服务并初始化
    必须使用 IMyIPCAidlInterface.Stub.asInterface(IBinder); 来初始化实例:

1
2
3
4
5
6
7
8
9
10
private boolean mAIDLIsBound = false;
private ServiceConnection mAIDLServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
mAIDLService = IMyIPCAidlInterface.Stub.asInterface(service);
mAIDLIsBound = true;
}
...
};
  • 创建 IMyAidlInterfaceCallback 实例
    private final IMyAidlInterfaceCallback mCallback = new IMyAidlInterfaceCallback.Stub(){...}

  • Callback 注册和取消注册

1
2
mAIDLService.registerCallback(mCallback);
mAIDLService.unregisterCallback(mCallback);

源码分析

编译器会根据 AIDL 自动生成对应的 Java 文件,根据服务端的 IMyIPCAidlInterface.aidl 文件生成的代码做源码分析。

文件路径及基本内容

app/build/generated/source/aidl/debug/***/ipc/IMyIPCAidlInterface.java

文件的类结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: ***\\ipc\\IMyIPCAidlInterface.aidl
*/
package ***.ipc;

public interface IMyIPCAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements ***.ipc.IMyIPCAidlInterface {
private static final java.lang.String DESCRIPTOR = "***.ipc.IMyIPCAidlInterface";
...
private static class Proxy implements ***.ipc.IMyIPCAidlInterface {
....
}
}
}

其中,DESCRIPTORBinder 的唯一表标识,一般用类名表示。

asInterface 的返回值

将服务端的 Binder 对象转成客户端的所需的 AIDL 对象:

1
2
3
4
5
6
7
8
9
10
public static ***.ipc.IMyIPCAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof ***.ipc.IMyIPCAidlInterface))) {
return ((***.ipc.IMyIPCAidlInterface) iin);
}
return new ***.ipc.IMyIPCAidlInterface.Stub.Proxy(obj);
}

从上面的源码可以看出,如果客户端和服务端:

  • 在相同进程下
    返回 Stub 对象本身。因为此时根本不需要跨进称通信。那么直接调用 Stub 对象的接口就可以了,返回的实现就是服务端的 Stub 实现,也就是客户端直接调用了服务端的代码。
  • 跨进程
    返回 Stub.Proxy对象。该对象持有着远程的 Binder 引用,因为现在需要跨进程通信,所以如果调用 Stub.Proxy 的接口的话,那么它们都将是 IPC 调用,它会通过调用 transact 方法去与服务端通信。

in 走向标记分析

in 模式下,对象从客户端传递到服务端后,不会被服务端修改:

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
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
...
case TRANSACTION_updateBookIn: {
data.enforceInterface(DESCRIPTOR);
***.ipc.MyDataParcelable _arg0;
if ((0 != data.readInt())) {
// 根据形参传入的数据,重新生成 _arg0 对象
_arg0 = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
// 也就是客户端传递的参数在这作了中转,并没有直接传递给服务端
// 而是根据客户端传递参数,新建了一个_arg0 对象传递给服务端
***.ipc.MyDataParcelable _result = this.updateBookIn(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
// 服务端的返回值对象也不是直接传递的,也是通过 parcel 中转的
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
}

@Override
public ***.ipc.MyDataParcelable updateBookIn(***.ipc.MyDataParcelable myDataParcelable)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
***.ipc.MyDataParcelable _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((myDataParcelable != null)) {
_data.writeInt(1);
// 形参数据写入 _data
myDataParcelable.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
// IPC 通信,将参数通过 parcel 传递进去
mRemote.transact(Stub.TRANSACTION_updateBookIn, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
// 服务端的修改 _reply 重新生成 _result 并返回
_result = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
// in 标记类型,形参的修改不会影响实参
return _result;
}

out 走向标记分析

out 模式下,对象无法从客户端传递到服务端,Stub.Proxy 在中转时新建了一个空对象传递给服务端。服务端针对该空对象填充数据后,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
50
51
52
53
54
55
case TRANSACTION_updateBookOut: {
data.enforceInterface(DESCRIPTOR);
***.ipc.MyDataParcelable _arg0;
// out 模式下,new 了一个空对象
// 所以自定义 Parcelable 时,必须实现空构造函数
_arg0 = new ***.ipc.MyDataParcelable();
// 根据传递的空对象,调用服务端的具体实现
***.ipc.MyDataParcelable _result = this.updateBookOut(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
// 服务端的返回结果写入 reply
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
if ((_arg0 != null)) {
reply.writeInt(1);
// 服务端对空对象 _arg0 的修改,同步写入 reply
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}

@Override
public ***.ipc.MyDataParcelable updateBookOut(***.ipc.MyDataParcelable myDataParcelable)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
***.ipc.MyDataParcelable _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 客户端传递进来的对象,并没有传递给服务端,_data 和 _reply 都是空的 Parcel
mRemote.transact(Stub.TRANSACTION_updateBookOut, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
// 根据服务端的返回结果,生成一个对象作为返回值
_result = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
// 如果服务端对传进去的空对象填充了数据,该数据写回客户端
// 即服务端的修改会同步影响到客户端
// 自定义 Parcelable 时,必须实现 readFromParcel,这里会读取
myDataParcelable.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

inout 走向标记分析

inout 走向类似引用传递,对象从客户端传递到服务端,服务端的修改同步会影响到客户端 ,综合了 inout 的功能:

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
case TRANSACTION_updateBookInOut: {
data.enforceInterface(DESCRIPTOR);
***.ipc.MyDataParcelable _arg0;
if ((0 != data.readInt())) {
// 根据形参传入的数据,重新生成 _arg0 对象
_arg0 = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
***.ipc.MyDataParcelable _result = this.updateBookInOut(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
// 服务端的返回值对象通过 parcel 中转
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
if ((_arg0 != null)) {
reply.writeInt(1);
// 服务端对对象 _arg0 的修改,同步写入 reply
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}

@Override
public ***.ipc.MyDataParcelable updateBookInOut(***.ipc.MyDataParcelable myDataParcelable)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
***.ipc.MyDataParcelable _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((myDataParcelable != null)) {
_data.writeInt(1);
// inout 模式下,形参数据写入 _data
myDataParcelable.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
// IPC 通信,将参数通过 parcel 传递进去
mRemote.transact(Stub.TRANSACTION_updateBookInOut, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
// 服务端的返回值重新生成 _result 并返回
_result = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
// 服务端对传进去对象做修改,同步写回客户端
// 即服务端的修改会同步影响到客户端
myDataParcelable.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

AIDL 作为参数不需要指定走向

IMyAidlInterfaceCallback 也是一个 AIDL,它作为参数时不需要指定走向,参数传递类似对象的引用传递(IMyAidlInterfaceCallback 直接写入 Parcel,并通过 Parcel 来传递),并不涉及到对象的转存。

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
case TRANSACTION_registerCallback: {
data.enforceInterface(DESCRIPTOR);
com.ymzs.androidbasicknowledge.ipc.IMyAidlInterfaceCallback _arg0;
_arg0 = com.ymzs.androidbasicknowledge.ipc.IMyAidlInterfaceCallback
.Stub.asInterface(data.readStrongBinder());
this.registerCallback(_arg0);
reply.writeNoException();
return true;
}

@Override
public void registerCallback(com.ymzs.androidbasicknowledge.ipc.IMyAidlInterfaceCallback cb)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((cb != null)) ? (cb.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_registerCallback, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}

示例及 Log 分析

示例代码

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
// 客户端代码
// test updateBookIn
MyDataParcelable inData = RandomData.genMyDataParcelable();
Log.d(TAG, "updateMyParcelableData: inData = " + inData.toString());
MyDataParcelable aidlInData = mAIDLService.updateBookIn(inData);
Log.d(TAG, "updateMyParcelableData: 2 : inData = " + inData.toString());
Log.d(TAG, "updateMyParcelableData: aidlInData = " + aidlInData.toString());

// test updateBookOut
MyDataParcelable outData = RandomData.genMyDataParcelable();
Log.d(TAG, "updateMyParcelableData: outData = " + outData);
MyDataParcelable aidlOutData = mAIDLService.updateBookOut(outData);
Log.d(TAG, "updateMyParcelableData: 2 : outData = " + outData);
Log.d(TAG, "updateMyParcelableData: aidlOutData = " + aidlOutData.toString());

// test updateBookInOut
MyDataParcelable inOutData = RandomData.genMyDataParcelable();
Log.d(TAG, "updateMyParcelableData: inOutData = " + inOutData.toString());
MyDataParcelable aidlInOutData = mAIDLService.updateBookInOut(inOutData);
Log.d(TAG, "updateMyParcelableData: 2: inOutData = " + inOutData.toString());
Log.d(TAG, "updateMyParcelableData: aidlInOutData = " + aidlInOutData.toString());

// test ListAndMapTypes
List<String> stringList = new ArrayList<String>();
stringList.add("This ");stringList.add("is ");
stringList.add("String ");stringList.add("list!");
Map<String, Integer> stringIntegerMap = new HashMap<String, Integer>();
stringIntegerMap.put("key1", 1);stringIntegerMap.put("key2", 2);
stringIntegerMap.put("key3", 3);
Log.d(TAG, "updateMyParcelableData: stringList = " +
Utils.printList(stringList));
Log.d(TAG, "updateMyParcelableData: stringIntegerMap = " +
Utils.printMap(stringIntegerMap));
mAIDLService.testListAndMapTypes(stringList, stringIntegerMap);
Log.d(TAG, "updateMyParcelableData: update: stringList = " +
Utils.printList(stringList));
Log.d(TAG, "updateMyParcelableData: update: stringIntegerMap = " +
Utils.printMap(stringIntegerMap));

// 服务端代码
private MyDataParcelable updateBookIn(MyDataParcelable myDataParcelable){
Log.d(TAG, "updateBookIn: myDataParcelable = " + myDataParcelable);
String randomStr = RandomData.genRandomString(3);
Log.d(TAG, "updateBookIn: randomStr = " + randomStr);
myDataParcelable.setName(randomStr);
Log.d(TAG, "updateBookIn: myDataParcelable = " + myDataParcelable);

// 测试回调
sendCallbackMsg();

return myDataParcelable;
}

private MyDataParcelable updateBookOut(MyDataParcelable myDataParcelable){
if(myDataParcelable.getName() == null){
Log.d(TAG, "updateBookOut: It is AIDL type out! Parameter is null!");
//myDataParcelable = RandomData.genMyDataParcelable();
myDataParcelable.setName("aaa");
MyDataParcelable.Book book = new MyDataParcelable.Book(
RandomData.genRandomString(3), RandomData.genRandomNumber(100));
List<MyDataParcelable.Book> list = new ArrayList<MyDataParcelable.Book>();
list.add(book);
myDataParcelable.setBookList(list);
}
Log.d(TAG, "updateBookOut: myDataParcelable = " + myDataParcelable);
return myDataParcelable;
}

private MyDataParcelable updateBookInOut(MyDataParcelable myDataParcelable){
Log.d(TAG, "updateBookInOut: myDataParcelable = " + myDataParcelable);
String randomStr = RandomData.genRandomString(3);
Log.d(TAG, "updateBookInOut: randomStr = " + randomStr);
myDataParcelable.setName(randomStr);
Log.d(TAG, "updateBookInOut: myDataParcelable = " + myDataParcelable);
return myDataParcelable;
}

private void testListAndMapTypes(List<String> list, Map<String, Integer> map){
Log.d(TAG, "testListAndMapTypes: list = " + Utils.printList(list));
Log.d(TAG, "testListAndMapTypes: map = " + Utils.printMap(map));

list.add("Server!");
map.put("key4", 4);

Log.d(TAG, "testListAndMapTypes: update: list = " + Utils.printList(list));
Log.d(TAG, "testListAndMapTypes: update: map = " + Utils.printMap(map));
}

// 服务端回调
Log.d(TAG, "handleMessage: MSG_UPDATE_DATA");
MyDataParcelable data = RandomData.genMyDataParcelable();
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).updateData(data);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
Log.d(TAG, "handleMessage: MSG_UPDATE_DATA, Callback Done!");

Log 分析

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
// Activity 绑定服务
18:44:51.019 25369-25369/* D/:ShowIPCActivity:: onClick: button.onClick = Bind AIDL
18:44:51.019 25369-25369/* D/:ShowIPCActivity:: bindAIDLService: mAIDLIsBound = false
// 服务启动
18:44:51.145 25521-25521/*:aidl D/:MyAIDLService:: onCreate:
18:44:51.145 25521-25521/*:aidl D/:MyAIDLService:: onBind:
// 连接服务
18:44:51.149 25369-25369/* D/:ShowIPCActivity:: onServiceConnected:
// 注册 Activity 回调函数
18:44:51.153 25521-25542/*:aidl D/:MyAIDLService:: registerCallback:
// 点击 Button,调用服务的几个测试函数
18:44:52.808 25369-25369/* D/:ShowIPCActivity:: onClick: button.onClick = UpdateMyParcelableData
18:44:52.808 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData:
// 初始化实参并传递给服务端
18:44:52.811 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData:
inData = name: bbf, arrays: 1 2 3 , book.name: ehfid, book.price: 47
// 传递数据类型标记为 in,形参数据被传递到服务端
18:44:52.816 25521-25541/*:aidl D/:MyAIDLService:: updateBookIn:
myDataParcelable = name: bbf, arrays: 1 2 3 , book.name: ehfid, book.price: 47
// 服务端修改该对象
18:44:52.816 25521-25541/*:aidl D/:MyAIDLService:: updateBookIn: randomStr = eec
18:44:52.816 25521-25541/*:aidl D/:MyAIDLService:: updateBookIn:
myDataParcelable = name: eec, arrays: 1 2 3 , book.name: ehfid, book.price: 47
// 发送消息,准备回调客户端
18:44:52.816 25521-25541/*:aidl D/:MyAIDLService:: sendCallbackMsg: MSG_UPDATE_DATA
// in 模式下客户端对象并不会因为服务端修改而受影响
18:44:52.818 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData: 2 :
inData = name: bbf, arrays: 1 2 3 , book.name: ehfid, book.price: 47
// 服务端返回值
18:44:52.818 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData:
aidlInData = name: eec, arrays: 1 2 3 , book.name: ehfid, book.price: 47
// 重新初始化实参传递给服务端
18:44:52.818 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData:
outData = name: dab, arrays: 1 2 3 , book.name: cbefi, book.price: 36
// 传递数据类型标记为 out,客户端对象无法传递到服务端,只传了一个空对象
18:44:52.819 25521-25542/*:aidl D/:MyAIDLService:: updateBookOut: It is AIDL type out! Parameter is null!
// 服务端为该空对象填充数据
18:44:52.819 25521-25542/*:aidl D/:MyAIDLService:: updateBookOut:
myDataParcelable = name: aaa, arrays: 0 0 0 , book.name: fhi, book.price: 92
// out 模式下,服务端的修改会同步写回客户端
18:44:52.820 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData: 2 :
outData = name: aaa, arrays: 0 0 0 , book.name: fhi, book.price: 92
// 服务端返回值
18:44:52.820 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData:
aidlOutData = name: aaa, arrays: 0 0 0 , book.name: fhi, book.price: 92
// 重新初始化实参传递给服务端
18:44:52.820 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData:
inOutData = name: igf, arrays: 1 2 3 , book.name: gdbaa, book.price: 30
// 传递数据类型标记为 inout,形参数据被传递到服务端
18:44:52.820 25521-25541/*:aidl D/:MyAIDLService:: updateBookInOut:
myDataParcelable = name: igf, arrays: 1 2 3 , book.name: gdbaa, book.price: 30
// 服务端修改该对象
18:44:52.820 25521-25541/*:aidl D/:MyAIDLService:: updateBookInOut: randomStr = hcb
// 服务端返回值
18:44:52.821 25521-25541/*:aidl D/:MyAIDLService:: updateBookInOut:
myDataParcelable = name: hcb, arrays: 1 2 3 , book.name: gdbaa, book.price: 30
// inout 模式下,服务端的修改会同步写回客户端
18:44:52.821 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData: 2:
inOutData = name: hcb, arrays: 1 2 3 , book.name: gdbaa, book.price: 30
18:44:52.821 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData:
aidlInOutData = name: hcb, arrays: 1 2 3 , book.name: gdbaa, book.price: 30
// 测试集合类型 List 和 Map,并标记为 inout 类型
18:44:52.822 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData: stringList = This is String list!
18:44:52.822 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData: stringIntegerMap = key2:2 key1:1 key3:3
18:44:52.823 25521-25542/*:aidl D/:MyAIDLService:: testListAndMapTypes: list = This is String list!
18:44:52.823 25521-25542/*:aidl D/:MyAIDLService:: testListAndMapTypes: map = key2:2 key1:1 key3:3
18:44:52.823 25521-25542/*:aidl D/:MyAIDLService:: testListAndMapTypes: update: list = This is String list!Server!
18:44:52.824 25521-25542/*:aidl D/:MyAIDLService:: testListAndMapTypes: update: map = key2:2 key4:4 key1:1 key3:3
// 服务端的修改会同步写回客户端
18:44:52.824 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData: update: stringList = This is String list!Server!
18:44:52.824 25369-25369/* D/:ShowIPCActivity:: updateMyParcelableData: update: stringIntegerMap = key2:2 key4:4 key1:1 key3:3
// 收到消息,服务端回调所有客户端,回调函数被 oneway 关键字定义,程序不会阻塞
18:44:54.817 25521-25521/*:aidl D/:MyAIDLService:: handleMessage: MSG_UPDATE_DATA
18:44:54.817 25521-25521/*:aidl D/:MyAIDLService:: handleMessage: MSG_UPDATE_DATA, Callback Done!
// 客户端响应回调
18:44:54.818 25369-25389/* D/:ShowIPCActivity:: updateData:
myDataParcelable = name: cgi, arrays: 1 2 3 , book.name: gdhhb, book.price: 16
// 客户端回调执行完毕
18:44:57.818 25369-25389/* D/:ShowIPCActivity:: updateData: done!
18:45:00.052 25369-25369/* D/:ShowIPCActivity:: onClick: button.onClick = UnBind AIDL
// 解除服务绑定
18:45:00.052 25369-25369/* D/:ShowIPCActivity:: unBindAIDLService: mAIDLIsBound = true
18:45:00.057 25521-25541/*:aidl D/:MyAIDLService:: unregisterCallback:
18:45:00.069 25521-25521/*:aidl D/:MyAIDLService:: onDestroy:

结论

  • 如果是在相同进程中,只需要使用本地扩展 Binder
  • 如果跨进程通信并不考虑并发,可以使用 Messenger 实现通信,但是该方式只支持异步通信
  • 如果跨进程通信需要并发以及同步调用,则必须使用 AIDL 来通信

传输大小限制

默认跨进程通信,传输大小为 1M
The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process.
链接:https://developer.android.com/reference/android/os/TransactionTooLargeException.html

参考文档

  1. https://developer.android.com/guide/components/aidl.html
  2. http://blog.csdn.net/luoyanglizi/article/details/51980630
  3. http://blog.csdn.net/jiwangkailai02/article/details/48098087
  4. http://blog.csdn.net/scnuxisan225/article/details/49970217
  5. https://my.oschina.net/zhoulc/blog/199199
  6. http://blog.csdn.net/luoyanglizi/article/details/51958091
  7. https://stackoverflow.com/questions/5507990/how-to-return-hashmap-in-an-aidl-file
  8. http://www.dre.vanderbilt.edu/~schmidt/cs282/PDFs/8-Services-and-IPC-parts-14-15-and-16.pdf