概念 全称: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(); final int N = mCallbacks.beginBroadcast();for (int i=0 ; i<N; i++) { try { mCallbacks.getBroadcastItem(i).updateData(data); } catch (RemoteException e) { } } 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 package ***.ipc;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 package com.ymzs.androidbasicknowledge.ipc;import com.ymzs.androidbasicknowledge.ipc.MyDataParcelable;import com.ymzs.androidbasicknowledge.ipc.IMyAidlInterfaceCallback;interface IMyIPCAidlInterface { 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 final int N = mCallbacks.beginBroadcast();for (int i=0 ; i<N; i++) { try { mCallbacks.getBroadcastItem(i).updateData(data); } catch (RemoteException e) { } } mCallbacks.finishBroadcast();
客户端创建 AIDL
文件用于回调 客户端创建 IMyIPCAidlInterfaceCallback.aidl
文件,供服务端调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.ymzs.androidbasicknowledge.ipc;import com.ymzs.androidbasicknowledge.ipc.MyDataParcelable;oneway interface IMyAidlInterfaceCallback { void updateData (in MyDataParcelable myDataParcelable) ; }
客户端
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 ; } ... };
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 package ***.ipc;public interface IMyIPCAidlInterface extends android .os .IInterface { 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 { .... } } }
其中,DESCRIPTOR
是 Binder
的唯一表标识,一般用类名表示。
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 = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(data); } else { _arg0 = null ; } ***.ipc.MyDataParcelable _result = this .updateBookIn(_arg0); reply.writeNoException(); if ((_result != null )) { reply.writeInt(1 ); _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 ); myDataParcelable.writeToParcel(_data, 0 ); } else { _data.writeInt(0 ); } mRemote.transact(Stub.TRANSACTION_updateBookIn, _data, _reply, 0 ); _reply.readException(); if ((0 != _reply.readInt())) { _result = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(_reply); } else { _result = null ; } } finally { _reply.recycle(); _data.recycle(); } 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; _arg0 = new ***.ipc.MyDataParcelable(); ***.ipc.MyDataParcelable _result = this .updateBookOut(_arg0); reply.writeNoException(); if ((_result != null )) { reply.writeInt(1 ); _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0 ); } if ((_arg0 != null )) { reply.writeInt(1 ); _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); 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())) { myDataParcelable.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } return _result; }
inout
走向标记分析inout
走向类似引用传递,对象从客户端传递到服务端,服务端的修改同步会影响到客户端 ,综合了 in
和 out
的功能:
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 = ***.ipc.MyDataParcelable.CREATOR.createFromParcel(data); } else { _arg0 = null ; } ***.ipc.MyDataParcelable _result = this .updateBookInOut(_arg0); reply.writeNoException(); if ((_result != null )) { reply.writeInt(1 ); _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0 ); } if ((_arg0 != null )) { reply.writeInt(1 ); _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 ); myDataParcelable.writeToParcel(_data, 0 ); } else { _data.writeInt(0 ); } mRemote.transact(Stub.TRANSACTION_updateBookInOut, _data, _reply, 0 ); _reply.readException(); if ((0 != _reply.readInt())) { _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 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()); 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()); 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()); 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.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(); final int N = mCallbacks.beginBroadcast();for (int i=0 ; i<N; i++) { try { mCallbacks.getBroadcastItem(i).updateData(data); } catch (RemoteException e) { } } 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
参考文档
https://developer.android.com/guide/components/aidl.html
http://blog.csdn.net/luoyanglizi/article/details/51980630
http://blog.csdn.net/jiwangkailai02/article/details/48098087
http://blog.csdn.net/scnuxisan225/article/details/49970217
https://my.oschina.net/zhoulc/blog/199199
http://blog.csdn.net/luoyanglizi/article/details/51958091
https://stackoverflow.com/questions/5507990/how-to-return-hashmap-in-an-aidl-file
http://www.dre.vanderbilt.edu/~schmidt/cs282/PDFs/8-Services-and-IPC-parts-14-15-and-16.pdf