Android AIDL 实例与原理分析
为使应用程序之间能够彼此通信,Android 提供了 IPC (Inter Process Communication,进程间通信)的一种独特实现: AIDL (Android Interface Definition Language, Android接口定义语言)。
简单来说,AIDL 就是定义一个接口,客户端(调用端)通过 bindService 来与远程服务端建立一个连接,在该连接建立时会将返回一个 IBinder 对象,该对象是服务端 Binder 的 BinderProxy。在建立连接时,客户端通过 asInterface 函数将该 BinderProxy 对象包装成本地的 Proxy,并赋值给Proxy类的 mRemote 字段,本地通过 mRemote 即可调用远程方法。
1、创建 .aidl 文件
首先打开 Android Studio,new 一个 AIDL file。具体代码如下 :
interface IMyAidlInterface { /** * 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); }
basicTypes 方法事接口自带的,不过可以知道,在 aidl 中只能使用这些基本类型参数:int, long, boolean, float,double , String ;
除了 basicTypes 方法之外,我们也可以添加自己的方法。因此,可以删除 basicTypes 方法,添加自己的方法。
二、生成 .java 文件
添加完方法之后,选中 .aidl 文件,在弹出的菜单中选择 Synchronize LocalAIDLS... Service.java,就会会自动帮你生成对应的 java 代码。
格式化代码之后,如下所示:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/shenjiaqi/Documents/sjq/booksource/chapter6/DatabaseTest/app/src/main/aidl/com/example/databasetest/IMyAidlInterface.aidl */ package com.example.databasetest; public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.databasetest.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.example.databasetest.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) { return ((com.example.databasetest.IMyAidlInterface) iin); } return new com.example.databasetest.IMyAidlInterface.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_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 != data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.databasetest.IMyAidlInterface { 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; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString);
// 这里是重点,proxy 持有引用,这样就可以进行数据交换,也不会暴露这个对象 mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; }
如果,你需要修改 .aidl 文件,那么修改之后,选择 build -> make project 即可,会重新生成对应的java文件。
解释:
对于生成的这个java 类,很多刚接触的人会不理解,这里需要解释下:
-
IMyAidlInterface :这个是我们自己定义的一个 servier 接口,也就是将你想要有的功能定义在接口中;
-
IBinder:定义了与远程对象的交互协议,代表一种跨进程传输的能力,实现这个接口,就能将这个对象进行跨进程传递,但是如果要使用的话,推荐继承其子类 Binder;
-
Binder:实现了 IBinder 接口,代表的其实就是 Binder 本地对象。BinderProxy 类是 Binder 类的一个内部类,它代表远程进程的 Binder 对象的本地代理;这两个类都继承自IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。
-
Stub : AIDL 的时候,编译工具会给我们生成一个名为 Stub 的静态内部抽象类;这个类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口,表明它具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要开发者自己实现。
-
IInterface:IInterface 代表的就是 Server 进程对象具备什么样的能力(能提供哪些方法,其实对应的就是 AIDL 文件中定义的接口)
-
proxy: Stub 的静态内部类,是一个实现了 IMyAidlInterface 接口,所以他是一个远程代理对象,可以用于返回给客户端用。当 client 调用 proxy的某个方法的时候,会将参数传到 proxy 中,在通过其持有的远程实际对象,将方法名和参数等都传给远程实际对象,然后就会回调 onTransact,对应的方法就会被调用,以此来实现跨进程调用。
三、传输复杂数据
如果,需要传递复杂数据,那么就需要实现 Parcelable 接口,可序列化:
public class Info implements Parcelable { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Info() { } public Info(Parcel in) { content = in.readString(); } public static final Creator<Info> CREATOR = new Creator<Info>() { @Override public Info createFromParcel(Parcel in) { return new Info(in); } @Override public Info[] newArray(int size) { return new Info[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(content); } /** * 参数是一个Parcel,用它来存储与传输数据 * * @param dest */ public void readFromParcel(Parcel dest) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 content = dest.readString(); } //方便打印数据 @Override public String toString() { return "content : " + content; } }
与此同时,也要建一个 info.aidl 文件,表明数据也是可以传递的。
package com.viii.aidlclient; //注意:Info.Info.java的包名应当是一样的 //这个文件的作用是引入了一个序列化对象 Info 供其他的AIDL文件使用 //注意parcelable是小写 parcelable Info;
这样就可以使用 info 对象了。 不用在受前面的基本类型变量所控制。
四、建立 service
接下去,新建一个Service负责接收消息,并在AndroidManifest.xml里面注册 Service:
public class MyService extends Service { private static final String TAG = "MyService"; // private MyBinder mMyBinder = new MyBinder(); @Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind: ");
// 应该返回 mBinder return null; } @Override public void onCreate() { Log.d(TAG, "onCreate: "); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand: "); return super.onStartCommand(intent, flags, startId); } // 这里就是服务端的实现,继承了 stub,是提供给 client 调用的能力 private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { // 具体实现过程 } }; }
这时候,可以 basicTypes 方法添加具体函数代码,实现你想要的功能。
当我们在本地获取到代理后之后,调用 basicTypes 就会触发服务端的调用。
5、获取服务
接下去在 mainactivity 中进行绑定。
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private IMyAidlInterface mService; private boolean mIsBound; private AdditionServiceConnection mServiceConnection; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); doBindService() ; }/** * bind service */ private void doBindService() { mServiceConnection = new AdditionServiceConnection(); Intent intent = new Intent(this, MyService.class); bindService(intent, mServiceConnection, BIND_AUTO_CREATE); } /** * unbind service */ private void doUnbindService() { if (mIsBound) { unbindService(mServiceConnection); mServiceConnection = null; mIsBound = false; } } /** * ServiceConection */ class AdditionServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) {
// 连接的时候获取本地代理,这样我们就可以调用 service 中的方法了。 mService = IMyAidlInterface.Stub.asInterface((IBinder) service); mIsBound = true; try { //设置死亡代理 service.linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { e.printStackTrace(); } Log.d(TAG, "onServiceConnected: "); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; mIsBound = false; Log.d(TAG, "onServiceDisconnected: "); } } /** * 监听Binder是否死亡 */ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { if (mService == null) { return; } mService.asBinder().unlinkToDeath(mDeathRecipient, 0); mService = null; //重新绑定 doBindService(); } }; @Override protected void onStop() { super.onStop(); doUnbindService(); } }
将远程服务的 binder 拿到之后,我们就可以调用相关方法实现自己的功能呢。
到这里,一个 AIDL 就被我们实现了。
分析调用过程
看看 asInterface 方法,我们在 bind 一个 Service 之后,在 onServiceConnecttion 的回调里面,就是通过这个方法拿到一个远程的 service 的,这个方法做了什么呢?
/** * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) { return ((com.example.databasetest.IMyAidlInterface) iin); }
// 实际上,代理对象持有真实对象,同时代理对象会对数据进行处理后,再调用实体对象的方法 return new com.example.databasetest.IMyAidlInterface.Stub.Proxy(obj); }
首先看函数的参数 IBinder
类型的 obj,这个对象是驱动给我们的,如果是 Binder 本地对象,那么它就是 Binder 类型,如果是 Binder 代理对象,那就是 BinderProxy
类型;它会试着查找 Binder 本地对象,如果找到,说明 Client 和 Server 都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回。
如果找不到,说明是远程对象(处于另外一个进程)那么就需要创建一个 Binder 代理对象,让这个 Binder 代理实现对于远程对象的访问。一般来说,如果是与一个远程 Service 对象进行通信,那么这里返回的一定是一个 Binder 代理对象,这个 IBinder 参数的实际上是 BinderProxy;
再看看我们对于 aidl 的 basicTypes 方法的实现;在 Stub 类里面,basicTypes
是一个抽象方法,我们需要继承这个类并实现它;如果 Client 和 Server 在同一个进程,那么直接就是调用这个方法;那么,如果是远程调用,这中间发生了什么呢?Client 是如何调用到 Server 的方法的?
对于远程方法的调用,是通过 Binder 代理完成的,在这个例子里面就是 Proxy
类;Proxy
对于 basicTypes
方法的实现如下:
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); // 这里是重点,调用的是实体对象的方法 mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }
它首先用 Parcel 把数据序列化了,然后调用了 transact 方法;这个 transact 到底做了什么呢?这个 Proxy 类在 asInterface 方法里面被创建,前面提到过,如果是 Binder 代理那么说明驱动返回的 IBinder 实际是 BinderProxy,因此我们的 Proxy 类里面的 mRemote 实际类型应该是BinderProxy;我们看看 BinderProxy 的 transact 方法:( Binder.java 的内部类)
public native boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
这是一个本地方法;它的实现在 native 层,具体来说在 frameworks/base/core/jni/android_util_Binder.cpp 文件,里面进行了一系列的函数调用,调用链实在太长这里就不给出了;要知道的是它最终调用到了talkWithDriver函数;看这个函数的名字就知道,通信过程要交给驱动完成了;这个函数最后通过 ioctl 系统调用,Client 进程陷入内核态,Client 调用 basicTypes 方法的线程挂起等待返回;驱动完成一系列的操作之后唤醒 Server 进程,调用了Server进程本地对象的 onTransact 函数(实际上由 Server 端线程池完成)。我们再看 Binder 本地对象的 onTransact 方法(这里就是 Stub 类里面的此方法):
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_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 != data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); }
在 Server 进程里面,onTransact 根据调用号(每个 AIDL 函数都有一个编号,在跨进程的时候,不会传递函数,而是传递编号指明调用哪个函数)调用相关函数。
在这个例子里面,调用了 Binder 本地对象的 basicTypes 方法;这个方法将结果返回给驱动,驱动唤醒挂起的 Client 进程里面的线程并将结果返回。于是一次跨进程调用就完成了。
至此,你应该对 AIDL 这种通信方式里面的各个类以及各个角色有了一定的了解;它总是那么一种固定的模式:一个需要跨进程传递的对象一定继承自 IBinder,如果是 Binder 本地对象,那么一定继承 Binder 实现 IInterface,如果是代理对象,那么就实现了 IInterface 并持有了 IBinder 引用;
Proxy 与 Stub 不一样,虽然他们都既是 Binder 又是 IInterface,不同的是 Stub 采用的是继承(is 关系),Proxy采用的是组合(has 关系)。他们均实现了所有的 IInterface 函数。
不同的是,Stub又使用策略模式调用的是虚函数(待子类实现),而 Proxy 则使用组合模式。为什么Stub采用继承而 Proxy 采用组合?事实上,Stub 本身 is 一个 IBinder(Binder),它本身就是一个能跨越进程边界传输的对象,所以它得继承 IBinder 实现 transact 这个函数从而得到跨越进程的能力(这个能力由驱动赋予)。
Proxy 类使用组合,是因为他不关心自己是什么,它也不需要跨越进程传输,它只需要拥有这个能力即可,要拥有这个能力,只需要保留一个对 IBinder 的引用。
如果把这个过程做一个类比,在封建社会,Stub 好比皇帝,可以号令天下,他生而具有这个权利(不要说宣扬封建迷信。。)如果一个人也想号令天下,可以,“挟天子以令诸侯”。为什么不自己去当皇帝,其一,一般情况没必要,当了皇帝其实限制也蛮多的是不是?我现在既能掌管天下,又能不受约束(Java 单继承);其二,名不正言不顺啊,我本来特么就不是(Binder),你非要我是说不过去,搞不好还会造反。最后呢,如果想当皇帝也可以,那就是 asBinder了。在 Stub 类里面,asBinder 返回 this,在 Proxy 里面返回的是持有的组合类 IBinder 的引用。
具体实例代码见 :https://download.csdn.net/download/szengjiaqi/10613236