Binder
- 在安卓使用Binder實現進程間通信需要做哪些工作
- 如何模糊跨進程調用與進程內調用?
- 如何使用AIDL
如何利用Binder實現進程間通信
我們先看下Binder調用大致原理,這是Binder調用的標準調用過程,我們下面的代碼將逐漸從不標準過程轉成標準過程:
首先,我們 android studio 新建兩個工程(兩個 moudle 也可以,這里目的為創建兩個運行在不同進程的app),一個Server,一個Client,而后,在Server中,我們新建一個java類Stub(類名無所謂),繼承android.os.Binder,之后重寫onTransact 方法,此處注意,onTransact方法的四個參數:
- code:方法標識符,因為Client端對Server端的所有調用都會走到Server端的這個方法,所以理所應當Client端應該傳遞一個參數過來用以表示要調用哪個方法,注意這個int類型的標識必須介于 FIRST_CALL_TRANSACTION 和 LAST_CALL_TRANSACTION之間,所以我們給方法分配code的時候最好使用FIRST_CALL_TRANSACTION+n 這種方式
- data :Client傳遞過來的序列化數據包,Parcel類型
- reply: 如果Client端調用時需要返回值,Server通過這個對象將返回值傳遞回去,同樣Parcel類型
- flag 用來區分這個調用是普通調用還是單邊調用,普通調用時,Client端線程會阻塞,直到從Server端接收到返回值(所以如果Client端是主線程調用,其調用的Server端不宜做耗時操作,這會讓造成Client的ANR),若flag==IBinder.FLAG_ONEWAY,則這次調用是單邊調用,Client在傳出數據后會立即執行下一段代碼,此時兩端異步執行,單邊調用時函數返回值必須為void (也就是異步調用必須舍棄返回值,要返回值就必須阻塞等待)
有以上,Server端的功能就已經可以實現,但在兩端通信時,為了兩端Binder匹配,我們還需要在Server端做一次驗證,用到data.enforceInterface(DESCRIPTOR)這個方法,DESCRIPTOR是Binder描述符,Binder Server和Client之間將通過這個描述符做驗證,要想通過驗證Binder通信的兩端DESCRIPTOR必須相同,這也是為什么我們在使用AIDL幫助我們生成Binder代碼的時候,必須把AIDL放在相同的包名下,因為SDK會根據包名為我們生成對應的DESCRIPTOR字符串,這里我們手寫Binder,只需要保證兩端相同就好了,包名字符串不是必須的
下面為Server端完整代碼
public class Stub extends android.os.Binder {
//用于標識調用的Binder
private static final java.lang.String DESCRIPTOR = "MyBinder";
//方法標識,這里我們準備兩個方法,一個無參,一個有參
private static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
private static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case TRANSACTION_method0: {
//Store or read an IBinder interface token
//驗證Binder標識
data.enforceInterface(DESCRIPTOR);
//調用實現方法
this.method0();
reply.writeNoException();
return true;
}
case TRANSACTION_method1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
//按寫入的順序讀取數據
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.method1(_arg0, _arg1);
reply.writeNoException();
//向Client寫回返回值
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//Server端真正實現業務的兩個方法
public void method0() throws RemoteException {
Log.e("Server", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method0");
}
public int method1(int a, int b) throws RemoteException {
Log.e("Server", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method1" + " " + a + " " + b);
return a + b;
}
}
應用間要實現Binder通信必須要用Service來完成,想象客戶端要怎樣才能知道服務端的Binder地址并向其寫入數據,一種是客戶端通過一個Binder地址總管查詢,通過鍵名查找到對應的Binder服務,這種方式就是有名Binder,這個總管類就是ServiceManager,應用進程獲取系統服務就是通過查詢這個Binder總管實現的,比如應用進程啟動進入java層后就會去查找AMS的客戶端,就是通過ServiceManager來查找的,但作為應用進程,是不能向ServiceManager注冊有名Binder的,所以我們的客戶端也沒法通過ServiceManager查詢到對應的Binder服務端,但應用進程間依然是可以獲取到對方的Binder服務端的,Binder并不一定要注冊到ServiceManager才能被獲取到,這種Binder的獲取方式就是通過已經獲取到的Binder傳遞Binder,也就是說如果有某個有名Binder服務它提供了傳遞Binder的方法,那么我們就可以通過這個Binder服務來傳遞我們的匿名Binder,正好,AMS作為一個有名Binder提供了這個功能,其對Binder傳遞被封裝到了Service組件當中,我們可以通過Service.onBind 來返回我們要傳遞的匿名Binder客戶端,而在Activity.bindService中獲取到這個Binder:
@Override
public IBinder onBind(Intent intent) {
return new Stub();
}
在Client端Activity 中bindService,我們來看Activity的代碼:
//定義常量
//注意兩個工程中對應的標識符必須相同
static final String DESCRIPTOR = "MyBinder";
//方法標識
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.xxx.server", "com.xxx.server.ServerService"));
boolean b = bindService(intent, conn, BIND_AUTO_CREATE);
Log.e("Client", " "+b);
}
bindService 的第二個參數,ServiceConnnection,在這個回調中我們取得IBinder對象,這個對象是Server端在Client中的一個代理對象
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
//這個IBinder對象iBinder,可以調用c++層Binder代理并最終通過Binder驅動傳遞數據
Parcel _data0 = Parcel.obtain();//申請傳遞參數的Parcel對象(從Parcel池中取出)
Parcel _reply0 = Parcel.obtain();//申請接收返回值的Parcel對象,相當于數據載體
Parcel _data1 = Parcel.obtain();
Parcel _reply1 = Parcel.obtain();
try {
//調用第一個方法
//寫入Binder標識,以便服務端驗證
_data0.writeInterfaceToken(DESCRIPTOR);
//傳入方法標識,以便服務端知道我們要調用哪個方法,注意最后一個參數,就是上面提到的 //flag,如果我們傳入IBinder.FLAG_ONEWAY,則這次調用為單邊調用,這個方法會立即返 //回,不會等服務端方法返回
iBinder.transact(TRANSACTION_method0, _data0, _reply0, 0);
_reply0.readException();
//調用第二個方法
_data1.writeInterfaceToken(DESCRIPTOR);
//按順序寫入參數
_data1.writeInt(1);
_data1.writeInt(2);
//從下面這行代碼開始本線程會阻塞,直到服務端進程中調用的方法完成計算返回后這個線程繼 //續運行,計算的返回值放入_reply1中
iBinder.transact(TRANSACTION_method1, _data1, _reply1, 0);
_reply1.readException();
int i = _reply1.readInt();//從reply中讀取返回值,這里我們就得到了服務端計算后的結果
} catch (RemoteException e) {
e.printStackTrace();
} finally {
//回收Parcel
_data0.recycle();
_reply0.recycle();
_data1.recycle();
_reply1.recycle();
}
Log.e("Client", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method0");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
通過以上代碼,我們就可以實現跨進程間的方法調用
我們可以對onServiceConnected方法里的代碼做一定封裝,使用Proxy類封裝對IBinder的操作,使得調用的時候更方便
public class Proxy {
static final String DESCRIPTOR = "MyBinder";
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
public void method0() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_method0, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public int method1(int a, int b) 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.writeInt(a);
_data.writeInt(b);
mRemote.transact(TRANSACTION_method1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
此時ServiceConnection對象可以更改為
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
Proxy proxy = new Proxy(iBinder);
try {
proxy.method0();
int i = proxy.method1(1,2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
如何模糊跨進程調用與進程內調用?
分析問題
- onServiceConnected(ComponentName name, IBinder iBinder) 這個方法傳遞的IBinder接口對象是什么,在同進程與不同進程時有什么不一樣?
- 如果通過bindService 傳遞過來的IBinder對象是同進程的,那我們還需要使用IBinder.transact傳遞數據嗎?要知道的Binder的使用需要層層調用并最終在內核空間進行一次數據復制
- 如果我們想跨進程的時候創建 Proxy 類包裹 IBinder 對象的操作,同進程的時候直接強轉 IBinder 對象為我們定義的對象或接口,不通過代理類直接使用其方法,應該怎么做?
對第一個問題,我們在 asInterface 里面打印一下傳進來的IBinder實例是什么類型,發現如果是遠程調用,傳給我們的 iBinder 是 BinderProxy 類型,他在native層會對應一個C++的BpBinder,BpBinder 最終會通過Binder驅動跟Server端通信。如果是本地調用,打印出的類型為Stub,說明本地調用時,onServiceConnected傳過來的就是我們在Service的onBinde方法返回的Stub對象本身。在這個基礎上,為了讓遠程調用(通過我們新建Proxy封裝BinderProxy對象)和本地調用(直接調用繼承自Binder的Stub對象)統一,我們讓Proxy和Stub實現相同的接口,再實現一個靜態方法,根據傳遞的IBinder對象返回一個對象,這個對象實現我們定義的接口,在進程內調用時,這個對象就是Stub類及其子類對象,當跨進程調用時,這個對象就是Proxy實例,由于要考慮兩種情況,我們就需要在這個靜態方法中作出判斷,判斷傳遞的IBinder對象是本地對象還是遠程對象,再根據判斷決定直接強轉為我們定義的接口返回,或生成Proxy對象強轉返回。此時我們需要將兩份文件合并。
怎樣區分IBinder對象的具體類型,我們可以通過IBinder的queryLocalInterface(DESCRIPTOR)方法,得到IInterface對象,判斷是否為null:
//定義靜態方法根據傳遞的IBinder對象返回實現相同接口的不同對象
public static IMyInterface asInterface(IBinder iBinder){
if ((iBinder == null)) {
return null;
}
IInterface iin = iBinder.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IMyInterface))) {
return ((IMyInterface)iin );
}
return new Proxy(iBinder);
}
若為Stub則返回值就是Stub實例本身,讓我們看一下Binder.queryLocalInterface的實現
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
* descriptor.
*/
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
若為代理類BindProxy則為空,我們看下其實現:
public IInterface queryLocalInterface(String descriptor) {
return null;
}
將傳進來的descriptor與mDescriptor比較,若相同,說明這是進程內調用,返回mOwner,這個mOwner和mDescriptor是需要Binder調用attachInterface賦值,所以我們在Stub構造方法里調用這個方法,又因為這個方法需要的參數為IInterface,所以我們讓Stub實現這個接口,最后由于我們已經將Proxy和Stub文件合并,在我們需要給別的進程綁定我們的iBinder時,需要把這個文件添加到對應的應用里,我們這個就需要把Stub兩個方法的實現抽離出來,具體的實現放在我們的業務代碼里,讓Server端Stub子類去實現
public interface IMyInterface {
//定義兩個方法
void method0() throws RemoteException;
int method1(int a, int b) throws RemoteException;
//用于標示調用的Binder
static final java.lang.String DESCRIPTOR = "MyBinder";
//用于標識調用的方法
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
//定義靜態方法根據傳遞的IBinder對象返回實現相同接口的不同對象
public static IMyInterface asInterface(IBinder iBinder){
if ((iBinder == null)) {
return null;
}
IInterface iin = iBinder.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IMyInterface))) {
return ((IMyInterface)iin );
}
return new Proxy(iBinder);
}
public abstract class Stub extends android.os.Binder implements IMyInterface,IInterface {
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
@Override
public 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 TRANSACTION_method0: {
data.enforceInterface(DESCRIPTOR);//Store or read an IBinder interface token
this.method0(); //
reply.writeNoException();
return true;
}
case TRANSACTION_method1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt(); //按寫入的順序讀取數據
int _arg1;
_arg1 = data.readInt();
int _result = this.method1(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result); //向Client寫回返回值
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
public class Proxy implements IMyInterface {
static final String DESCRIPTOR = "MyBinder";
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public void method0() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_method0, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int method1(int a, int b) 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.writeInt(a);
_data.writeInt(b);
mRemote.transact(TRANSACTION_method1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
將如上文件放在需要通信的兩個工程里,就可以實現Binder通信,如果同進程調用,就會直接使用返回的對象而不通過Binder機制。
可以看出調用總是由客戶端發起,由服務端運算結束。
怎么實現進程間互調?
Binder被設計為CS模式,其本身是不支持服務端主動調客戶端的,但我們可以有一些曲線救國的方式,觀察Parcel這個類,它是支持傳遞IBinder和IInterface接口的,我們可以在Client調用Server時將Client端定義的Binder服務傳遞至Server,Server端拿到這個Binder地址,就可以在服務端也創建一個BinderProxy,就可以像Client端調用它一樣調用Client端的Binder,這時雙方角色互換,應用進程啟動時就是這樣把自己的ApplicationThread 通過AMS傳遞給system_server進程的,所以system_server在應用啟動并主動綁定AMS后就可以通過ApplicationThreadProxy來遠程調用應用的方法從而管理應用了。
觀察Parcel傳遞IInterface這個方法
public final void writeStrongInterface(IInterface val) {
writeStrongBinder(val == null ? null : val.asBinder());
}
實際還是傳遞的 IBinder,這里回去看我們實現的Binder通信的代碼,我們只讓 Stub 實現了 IInterface ,然而我們希望在傳遞 IInterface 時不用去區分是 Stub 還是 Proxy,我們讓 Proxy 也實現IInterface這個接口,現在Stub和Proxy都實現我們定義的IMyInterface和系統提供的IInterface這兩個接口,所以我們讓IMyInterface直接繼承IInterface就好了,現在Proxy需要重寫IInterface的asBinder,返回mRemote變量就好了。至此,我們完成一份完整的 Binder 封裝代碼,這份代碼和我們編寫 IMyInterface.aidl 文件編譯后編譯器為我們生成的 java 文件是一樣的,下面是使用Android Studio編寫aidl文件后編譯生成的java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /home/end/AndroidStudioProjects/Demo/demo2/src/main/aidl/com/xjh/end/demo2/IMyAidlInterface.aidl
*/
package com.xjh.demo;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.xjh.demo.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.xjh.demo.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
//可以用于區分此次調用是進程內還是進程間,因為進程內調用的,Stub 子類對象也就是
//服務端實例 的構造函數被調用過程中將 DESCRIPTOR 保存為了自己的成員變量,所以調用
//obj.queryLocalInterface(DESCRIPTOR)得到的結果不為空(實例實現是返回Binder子類也 //是Stub子類對象本身),如果是代理Binder端,之前的代碼可以看出BinderProxy類重新的方法 //直接返回就是null,這就可以區分當前調用是
//進程內還是進程間了
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.xjh.demo.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.xjh.demo.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.xjh.demo.IMyAidlInterface))) {
return ((com.xjh.demo.IMyAidlInterface) iin);
//若是進程內調用,直接強轉返回就可以
}
//若為進程間調用,需要用一個代理類封裝,這個代理類封裝了往Binder發消息的代碼,使得調用
//想進程內調用一樣方便
return new com.xjh.demo.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_method0: {
data.enforceInterface(DESCRIPTOR);
//調用子類實現
this.method0();
reply.writeNoException();
return true;
}
case TRANSACTION_method1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
//調用子類實現
int _result = this.method1(_arg0, _arg1);
reply.writeNoException();
//向客戶端寫入返回值
//android.os.Parcel data 和 reply 都只是數據的載體,至于數據具體是怎么
//通過Binder發送的,先不關心
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//aidl生成的文件把Stub作為IMyAidlInterface接口的內部類,又把Proxy作為Stub的內部類,但是 // 是不是內部并沒有關系,這兩個內部類都是靜態的,放在哪里都一樣,只是對外隱藏了這個代理類,使得
//使用者只能使用Stub.asInterface 來返回這個對象而不能直接使用這個類創建實例,使得對Binder
//進程內和進程間的訪問都被封裝成接口訪問,模糊兩者的區別,體現了更好的封裝。
private static class Proxy implements com.xjh.demo.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
//就是BinderProxy類的實例
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
//Proxy類的作用就是封裝了遠程調用時想Binder寫入數據的操作,
//這里仍然以android.os.Parcel類對象作為數據載體
@Override
public void method0() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
//寫入描述符,以通過服務端驗證
_data.writeInterfaceToken(DESCRIPTOR);
//將數據寫入Binder
//數據的寫入最終就是寫入Binder驅動,數據寫入內核空間,而
//由于Binder的機制,有部分內核空間和用戶空間的邏輯地址映射到了同一塊物理地址,
//所以服務端進程不需要再把數據復制到用戶空間,
//這也是Binder進行進程間通信效率高的原因之一,
//只經過了一次數據拷貝,而像Socket,則需要經過兩次拷貝,
//先從A進程將數據寫入內核空間,由于進程間在內核空間共享邏輯地址,
//所以B進程在內核空間也可以訪問到這個數據,但由于沒有像Binder一樣做內存映射,
//進程的內核空間和用戶空間的邏輯地址不共享,
//在B進程的用戶空間就訪問不到這個數據,
//所以還要從內核空間再將數據拷貝到B進程的用戶空間,完成一次跨進程數據傳遞
mRemote.transact(Stub.TRANSACTION_method0, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int method1(int a, int b) 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.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_method1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
//這就是Stub未實現需要子類自己實現的方法,子類實現這兩個方法實現服務端邏輯
public void method0() throws android.os.RemoteException;
public int method1(int a, int b) throws android.os.RemoteException;
}
以上代碼算是我們分析AIDL的原理分析,我們定義的AIDL文件最終會生成以上代碼,可以看出數據在客戶端寫入和在服務端讀出都是用了Parcel作為載體,這個類作用很強大,其還內部封裝了對Serializable接口數據的Binder傳遞方法,我們會在另一篇文章中解讀這個類。
我們定義AIDL接口以后,只需要在Server端繼承Stub并實現我們定義的接口方法,在客戶端將傳遞過來的IBinder對象用 asInterface 方法封裝,然后我們就可以像使用本地對象一樣調用遠程對象了。
最終實現的效果,進程內調用時,就是直接調用實現類的方法(method0,method1),跨進程調用時,客戶端通過Proxy類往代理Binder寫值,在服務端進程里,再取出這些值,根據這些數據再去調用對應的 實現類的方法(method0,method1),在服務端,方法的調用是在Binder線程里進行的,遠程調用的每次調用在服務端都是在Binder線程里進行的,這些Binder線程由Binder線程池管理,也就是說如果是遠程調用,method0,method1是運行在Binder線程里的,那么如果想讓我們的調用在Server端主線程執行,我們需要在Server端主線程創建Handler來把消息通過handler再轉給主線程來實現,這是AMS管理應用組件生命周期的方式,這也是Android另一個進程間通信方式Messager的原理,其實就是封裝了Binder和Handler,Binder跨進程發過來的消息立即轉到Handler,再用Handler把消息從Binder線程轉到指定線程,在指定線程中處理,這里我們即使不看代碼也應該可以判斷,Message的消息發送都是單邊調用,消息一旦發出不會等待調用結果返回。
要注意的是,如果我們是在App Process的UI thread 里面雙邊調用遠程對象,顯然和調用本地對象一樣,這個調用不能是耗時操作,UI thread 會等待遠端方法返回后再繼續運行。
這里貼一下以上java代碼對應的AIDL文件,SDK就是根據這個文件生成了以上一大串java代碼:
// IMyAidlInterface.aidl
package com.xjh.demo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
void method0();
int method1(int a, int b);
}
如何使用AIDL
AIDL接口支持哪些數據類型
- Java 基本數據類型
- String和CharSequence
- Parcelable
- List 和 Map(泛型類型必須是以上類型)
- AIDL接口
aidl的使用需要我們聲明對應的aidl接口,編譯時SDK會根據aidl文件生成相應的java代碼,其功能就是封裝Binder的操作,使跨進程調用和本地調用一樣方便,上面已經貼出傳遞基本類型的AIDL代碼,而對于對象的傳遞我們需要用到Parcelable接口,而在aidl接口中的方法,其 Parcelable 類型的參數不管是不是屬于同一個包都需要 import,aidl 接口也是,除此之外,形參還需要指定 in | out | inout 類型,基本類型默認且只能是in類型,out類型指Binder服務端不讀取從客戶端傳過來的數據,而直接創建空對象,在這次調用的末尾再把這個對象再傳遞回客戶端,inout就是接收客戶端傳過來的數據,生成對象,調用過后再把這個對象傳遞回去。
所有需要在Binder中傳遞的 Parcelable 的實現類都需要創建與其名相同的 aidl 文件,這個文件中不必寫接口和方法,如我要實現一個Book 類,實現Parcelable接口:
package com.xxx.xxx;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable{
public String name;
public int price;
protected Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
}
// Book.aidl
package com.xxx.xxx;
parcelable Book;
以上可知我們實現 Parcelable 這個接口,主要的工作就是把我們想要傳遞的對象包含的數據寫入 Parcel 以及從Parcel中取出數據生成對應的對象,其實還是把對象拆成基本類型,再在另一端再次生成對象,Binder底層傳遞的還是基本類型,其實對于安卓的另一個序列化接口Serializable,Parcel也就會將其反射取出數據裝入byte數組,然后在另一端取出來反射再生成對象,Binder底層傳遞的還是基本類型,我們將用一篇文章講解Parcel這個類。
復習下這張圖: