解析Android AIDL的實(shí)例與原理
一、概述
簡(jiǎn)單來(lái)說(shuō),AIDL 就是定義一個(gè)接口,客戶端(調(diào)用端)通過(guò) bindService 來(lái)與遠(yuǎn)程服務(wù)端建立一個(gè)連接,在該連接建立時(shí)會(huì)將返回一個(gè) IBinder 對(duì)象,該對(duì)象是服務(wù)端 Binder 的 BinderProxy。在建立連接時(shí),客戶端通過(guò) asInterface 函數(shù)將該 BinderProxy 對(duì)象包裝成本地的 Proxy,并賦值給Proxy類的 mRemote 字段,本地通過(guò) mRemote 即可調(diào)用遠(yuǎn)程方法。
二、創(chuàng)建 .aidl 文件
首先打開 Android Studio,new 一個(gè) 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 方法事接口自帶的,不過(guò)可以知道,在 aidl 中只能使用這些基本類型參數(shù):int, long, boolean, float,double, String ;
除了basicTypes 方法之外,我們也可以添加自己的方法。因此,可以刪除basicTypes 方法,添加自己的方法。
三、生成 .java 文件
添加完方法之后,選中 .aidl 文件,在彈出的菜單中選擇 Synchronize LocalAIDLS... Service.java,就會(huì)會(huì)自動(dòng)幫你生成對(duì)應(yīng)的 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); // 這里是重點(diǎn),proxy 持有引用,這樣就可以進(jìn)行數(shù)據(jù)交換,也不會(huì)暴露這個(gè)對(duì)象
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 即可,會(huì)重新生成對(duì)應(yīng)的java文件。
對(duì)于生成的這個(gè)java 類,很多剛接觸的人會(huì)不理解,這里需要解釋下:
- IMyAidlInterface :這個(gè)是我們自己定義的一個(gè) servier 接口,也就是將你想要有的功能定義在接口中;
- IBinder:定義了與遠(yuǎn)程對(duì)象的交互協(xié)議,代表一種跨進(jìn)程傳輸?shù)哪芰?,?shí)現(xiàn)這個(gè)接口,就能將這個(gè)對(duì)象進(jìn)行跨進(jìn)程傳遞,但是如果要使用的話,推薦繼承其子類 Binder;
- Binder:實(shí)現(xiàn)了 IBinder 接口,代表的其實(shí)就是Binder 本地對(duì)象。BinderProxy 類是 Binder 類的一個(gè)內(nèi)部類,它代表遠(yuǎn)程進(jìn)程的 Binder 對(duì)象的本地代理;這兩個(gè)類都繼承自IBinder, 因而都具有跨進(jìn)程傳輸?shù)哪芰?;?shí)際上,在跨越進(jìn)程的時(shí)候,Binder 驅(qū)動(dòng)會(huì)自動(dòng)完成這兩個(gè)對(duì)象的轉(zhuǎn)換。
- Stub: AIDL 的時(shí)候,編譯工具會(huì)給我們生成一個(gè)名為 Stub 的靜態(tài)內(nèi)部抽象類;這個(gè)類繼承了 Binder, 說(shuō)明它是一個(gè) Binder 本地對(duì)象,它實(shí)現(xiàn)了 IInterface 接口,表明它具有 Server 承諾給 Client 的能力;Stub 是一個(gè)抽象類,具體的 IInterface 的相關(guān)實(shí)現(xiàn)需要開發(fā)者自己實(shí)現(xiàn)。
- IInterface:IInterface 代表的就是 Server 進(jìn)程對(duì)象具備什么樣的能力(能提供哪些方法,其實(shí)對(duì)應(yīng)的就是 AIDL 文件中定義的接口)
- proxy:Stub 的靜態(tài)內(nèi)部類,是一個(gè)實(shí)現(xiàn)了IMyAidlInterface接口,所以他是一個(gè)遠(yuǎn)程代理對(duì)象,可以用于返回給客戶端用。當(dāng) client 調(diào)用 proxy的某個(gè)方法的時(shí)候,會(huì)將參數(shù)傳到 proxy 中,在通過(guò)其持有的遠(yuǎn)程實(shí)際對(duì)象,將方法名和參數(shù)等都傳給遠(yuǎn)程實(shí)際對(duì)象,然后就會(huì)回調(diào)onTransact,對(duì)應(yīng)的方法就會(huì)被調(diào)用,以此來(lái)實(shí)現(xiàn)跨進(jìn)程調(diào)用。
四、傳輸復(fù)雜數(shù)據(jù)
如果,需要傳遞復(fù)雜數(shù)據(jù),那么就需要實(shí)現(xiàn)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);
}
/**
* 參數(shù)是一個(gè)Parcel,用它來(lái)存儲(chǔ)與傳輸數(shù)據(jù)
*
* @param dest
*/
public void readFromParcel(Parcel dest) {
//注意,此處的讀值順序應(yīng)當(dāng)是和writeToParcel()方法中一致的
content = dest.readString();
}
//方便打印數(shù)據(jù)
@Override
public String toString() {
return "content : " + content;
}
}
與此同時(shí),也要建一個(gè) info.aidl 文件,表明數(shù)據(jù)也是可以傳遞的。
package com.viii.aidlclient; //注意:Info.Info.java的包名應(yīng)當(dāng)是一樣的 //這個(gè)文件的作用是引入了一個(gè)序列化對(duì)象 Info 供其他的AIDL文件使用 //注意parcelable是小寫 parcelable Info;
這樣就可以使用 info 對(duì)象了。 不用在受前面的基本類型變量所控制。
五、建立 service
接下去,新建一個(gè)Service負(fù)責(zé)接收消息,并在AndroidManifest.xml里面注冊(cè) 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: "); // 應(yīng)該返回 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);
}
// 這里就是服務(wù)端的實(shí)現(xiàn),繼承了 stub,想要怎么樣的能力,自己去實(shí)現(xiàn)
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 {
// 具體實(shí)現(xiàn)過(guò)程
}
};
}
這時(shí)候,可以basicTypes 方法添加具體函數(shù)代碼,實(shí)現(xiàn)你想要的功能。
當(dāng)我們?cè)诒镜孬@取到代理后之后,調(diào)用basicTypes 就會(huì)觸發(fā)服務(wù)端的調(diào)用。
六、獲取服務(wù)
接下去在 mainactivity 中進(jìn)行綁定。
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) { // 連接的時(shí)候獲取本地代理,這樣我們就可以調(diào)用 service 中的方法了。
mService = IMyAidlInterface.Stub.asInterface((IBinder) service);
mIsBound = true;
try {
//設(shè)置死亡代理
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: ");
}
}
/**
* 監(jiān)聽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();
}
}
將遠(yuǎn)程服務(wù)的 binder 拿到之后,我們就可以調(diào)用相關(guān)方法實(shí)現(xiàn)自己的功能呢。
到這里,一個(gè) AIDL 就被我們實(shí)現(xiàn)了。
七、分析調(diào)用過(guò)程
看看 asInterface 方法,我們?cè)?bind 一個(gè) Service 之后,在 onServiceConnecttion 的回調(diào)里面,就是通過(guò)這個(gè)方法拿到一個(gè)遠(yuǎn)程的 service 的,這個(gè)方法做了什么呢?
/**
* 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);
} // 實(shí)際上,代理對(duì)象持有真實(shí)對(duì)象,同時(shí)代理對(duì)象會(huì)對(duì)數(shù)據(jù)進(jìn)行處理后,再調(diào)用實(shí)體對(duì)象的方法
return new com.example.databasetest.IMyAidlInterface.Stub.Proxy(obj);
}
首先看函數(shù)的參數(shù)IBinder類型的 obj,這個(gè)對(duì)象是驅(qū)動(dòng)給我們的,如果是 Binder 本地對(duì)象,那么它就是 Binder 類型,如果是 Binder 代理對(duì)象,那就是BinderProxy類型;它會(huì)試著查找 Binder 本地對(duì)象,如果找到,說(shuō)明 Client 和 Server 都在同一個(gè)進(jìn)程,這個(gè)參數(shù)直接就是本地對(duì)象,直接強(qiáng)制類型轉(zhuǎn)換然后返回。
如果找不到,說(shuō)明是遠(yuǎn)程對(duì)象(處于另外一個(gè)進(jìn)程)那么就需要?jiǎng)?chuàng)建一個(gè) Binder 代理對(duì)象,讓這個(gè) Binder 代理實(shí)現(xiàn)對(duì)于遠(yuǎn)程對(duì)象的訪問(wèn)。一般來(lái)說(shuō),如果是與一個(gè)遠(yuǎn)程 Service 對(duì)象進(jìn)行通信,那么這里返回的一定是一個(gè) Binder 代理對(duì)象,這個(gè) IBinder 參數(shù)的實(shí)際上是 BinderProxy;
再看看我們對(duì)于 aidl 的basicTypes方法的實(shí)現(xiàn);在 Stub 類里面,basicTypes是一個(gè)抽象方法,我們需要繼承這個(gè)類并實(shí)現(xiàn)它;如果 Client 和 Server 在同一個(gè)進(jìn)程,那么直接就是調(diào)用這個(gè)方法;那么,如果是遠(yuǎn)程調(diào)用,這中間發(fā)生了什么呢?Client 是如何調(diào)用到 Server 的方法的?
對(duì)于遠(yuǎn)程方法的調(diào)用,是通過(guò) Binder 代理完成的,在這個(gè)例子里面就是Proxy類;Proxy對(duì)于basicTypes方法的實(shí)現(xiàn)如下:
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);
// 這里是重點(diǎn),調(diào)用的是實(shí)體對(duì)象的方法
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
它首先用 Parcel 把數(shù)據(jù)序列化了,然后調(diào)用了 transact 方法;這個(gè) transact 到底做了什么呢?這個(gè) Proxy 類在 asInterface 方法里面被創(chuàng)建,前面提到過(guò),如果是 Binder 代理那么說(shuō)明驅(qū)動(dòng)返回的 IBinder 實(shí)際是 BinderProxy,因此我們的 Proxy 類里面的 mRemote 實(shí)際類型應(yīng)該是BinderProxy;我們看看 BinderProxy 的 transact 方法:( Binder.java 的內(nèi)部類)
public native boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
這是一個(gè)本地方法;它的實(shí)現(xiàn)在 native 層,具體來(lái)說(shuō)在 frameworks/base/core/jni/android_util_Binder.cpp 文件,里面進(jìn)行了一系列的函數(shù)調(diào)用,調(diào)用鏈實(shí)在太長(zhǎng)這里就不給出了;要知道的是它最終調(diào)用到了talkWithDriver函數(shù);看這個(gè)函數(shù)的名字就知道,通信過(guò)程要交給驅(qū)動(dòng)完成了;這個(gè)函數(shù)最后通過(guò) ioctl 系統(tǒng)調(diào)用,Client 進(jìn)程陷入內(nèi)核態(tài),Client 調(diào)用 basicTypes 方法的線程掛起等待返回;驅(qū)動(dòng)完成一系列的操作之后喚醒 Server 進(jìn)程,調(diào)用了Server進(jìn)程本地對(duì)象的 onTransact 函數(shù)(實(shí)際上由 Server 端線程池完成)。我們?cè)倏?Binder 本地對(duì)象的 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 進(jìn)程里面,onTransact 根據(jù)調(diào)用號(hào)(每個(gè) AIDL 函數(shù)都有一個(gè)編號(hào),在跨進(jìn)程的時(shí)候,不會(huì)傳遞函數(shù),而是傳遞編號(hào)指明調(diào)用哪個(gè)函數(shù))調(diào)用相關(guān)函數(shù)。
在這個(gè)例子里面,調(diào)用了 Binder 本地對(duì)象的 basicTypes 方法;這個(gè)方法將結(jié)果返回給驅(qū)動(dòng),驅(qū)動(dòng)喚醒掛起的 Client 進(jìn)程里面的線程并將結(jié)果返回。于是一次跨進(jìn)程調(diào)用就完成了。
至此,你應(yīng)該對(duì) AIDL 這種通信方式里面的各個(gè)類以及各個(gè)角色有了一定的了解;它總是那么一種固定的模式:一個(gè)需要跨進(jìn)程傳遞的對(duì)象一定繼承自 IBinder,如果是 Binder 本地對(duì)象,那么一定繼承 Binder 實(shí)現(xiàn) IInterface,如果是代理對(duì)象,那么就實(shí)現(xiàn)了 IInterface 并持有了 IBinder 引用;
Proxy 與 Stub 不一樣,雖然他們都既是 Binder 又是 IInterface,不同的是 Stub 采用的是繼承(is 關(guān)系),Proxy采用的是組合(has 關(guān)系)。他們均實(shí)現(xiàn)了所有的 IInterface 函數(shù)。
不同的是,Stub又使用策略模式調(diào)用的是虛函數(shù)(待子類實(shí)現(xiàn)),而 Proxy 則使用組合模式。為什么Stub采用繼承而 Proxy 采用組合?事實(shí)上,Stub 本身 is 一個(gè) IBinder(Binder),它本身就是一個(gè)能跨越進(jìn)程邊界傳輸?shù)膶?duì)象,所以它得繼承 IBinder 實(shí)現(xiàn) transact 這個(gè)函數(shù)從而得到跨越進(jìn)程的能力(這個(gè)能力由驅(qū)動(dòng)賦予)。
Proxy 類使用組合,是因?yàn)樗魂P(guān)心自己是什么,它也不需要跨越進(jìn)程傳輸,它只需要擁有這個(gè)能力即可,要擁有這個(gè)能力,只需要保留一個(gè)對(duì) IBinder 的引用。
以上就是解析Android AIDL的實(shí)例與原理的詳細(xì)內(nèi)容,更多關(guān)于Android AIDL的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實(shí)現(xiàn)旋轉(zhuǎn)動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)旋轉(zhuǎn)動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
簡(jiǎn)單實(shí)現(xiàn)Android計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了自己動(dòng)手實(shí)現(xiàn)的Android計(jì)算器功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
Android開發(fā)自學(xué)筆記(二):工程文件剖析
這篇文章主要介紹了Android開發(fā)自學(xué)筆記(二):工程文件剖析,本文講解了AndroidManifest.xml、src文件夾、res文件夾等文件的作用,需要的朋友可以參考下2015-04-04
Android應(yīng)用程序中讀寫txt文本文件的基本方法講解
這篇文章主要介紹了Android應(yīng)用程序中讀寫txt文本文件的基本方法講解,基本上依靠context.openFileInput()和context.openFileOutput()兩個(gè)方法為主,需要的朋友可以參考下2016-04-04
Android 提交或者上傳數(shù)據(jù)時(shí)的dialog彈框動(dòng)畫效果
我們?cè)谑褂弥Ц秾氈Ц兜臅r(shí)候會(huì)看到類似這種彈框動(dòng)畫效果,下面通過(guò)實(shí)例代碼給大家分享android 提交或者上傳數(shù)據(jù)時(shí)的彈框動(dòng)畫效果,感興趣的的朋友參考下2017-07-07
Kotlin協(xié)程開發(fā)之Flow的融合與Channel容量及溢出策略介紹
這篇文章主要介紹了Kotlin協(xié)程:Flow的融合、Channel容量、溢出策略,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09

