解析Android AIDL的實例與原理
一、概述
簡單來說,AIDL 就是定義一個接口,客戶端(調用端)通過 bindService 來與遠程服務端建立一個連接,在該連接建立時會將返回一個 IBinder 對象,該對象是服務端 Binder 的 BinderProxy。在建立連接時,客戶端通過 asInterface 函數將該 BinderProxy 對象包裝成本地的 Proxy,並賦值給Proxy類的 mRemote 字段,本地通過 mRemote 即可調用遠程方法。
二、創建 .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 代碼。
格式化代碼之後,如下所示:
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,想要怎麼樣的能力,自己去實現 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 就會觸發服務端的調用。
六、獲取服務
接下去在 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 的引用。
以上就是解析Android AIDL的實例與原理的詳細內容,更多關於Android AIDL的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 詳解Android中AIDL的使用
- 手把手教你Android全局觸摸事件監聽
- Android BindService使用案例講解
- Android掛斷電話最新實現方法
- 分析CmProcess跨進程通信的實現