aidl是android中非常重要的進(jìn)程間通信的方式毡泻,底層還是用的binder來實(shí)現(xiàn)的求冷。今天就重點(diǎn)講解一下aidl的使用和原理;
基礎(chǔ)使用
aidl本質(zhì)上也是采用的c/s架構(gòu),首先創(chuàng)建一個server端:
server端
- 1.右鍵生成aidl文件
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);
String getName();
void setName(String name);
}
basicTypes這個可以刪掉的护姆。這個方法是介紹了基礎(chǔ)的用法蝗砾,可以用哪些變量先较;后面就可以自定義方法了。
- 2.創(chuàng)建service
@Override
public IBinder onBind(Intent intent) {
return new Like();
}
class Like extends IMyAidlInterface.Stub{
@Override
public String getName() throws RemoteException {
return "i'm yours";
}
@Override
public void setName(String name) throws RemoteException {
}
}
創(chuàng)建一個內(nèi)部類遥诉,繼承自aidl.stub拇泣。然后重寫方法;
然后在onBind方法中return 這個內(nèi)部類的對象矮锈。
Client端
client端的寫法就很簡單了霉翔。
Intent intent= new Intent(action);
intent.setPackage(packagename);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
就是一個bindservice的過程;這樣兩個app就能通信啦苞笨。怎么樣债朵,是不是很簡單,接下來講下實(shí)現(xiàn)的原理瀑凝;
原理分析
要知道原理首先肯定得知道在編譯aidl文件時(shí)序芦,發(fā)生了什么。觀察看目錄知道其實(shí)是生成了一個同名的java文件粤咪,下面我們來研究這個java文件里都有啥谚中。
/**
public static class Default implements com.qikun.demo.IMyAidlInterface {
@Override
public void setName(java.lang.String name) throws android.os.RemoteException {
}
@Override
public java.lang.String getName() throws android.os.RemoteException {
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
首先生成了一個默認(rèn)的實(shí)現(xiàn)了此接口的類,叫default寥枝;
重點(diǎn)看stub類宪塔,此類實(shí)現(xiàn)了IMyAidlInterface的接口并繼承了binder。
public static com.qikun.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.qikun.demo.IMyAidlInterface))) {
return ((com.qikun.demo.IMyAidlInterface) iin);
}
return new com.qikun.demo.IMyAidlInterface.Stub.Proxy(obj);
}
將服務(wù)端的ibinder對象轉(zhuǎn)化為客戶端所需要的接口對象囊拜。
如果有必要的話某筐,產(chǎn)生一個代理對象Proxy。否則直接查詢本地的對象冠跷,就解決了同一個進(jìn)程里使用aidl的困境)
來分析一下這個proxy代理對象是怎么回事:
首先南誊,proxy構(gòu)造需要傳入一個ibinder對象身诺;
@Override
public java.lang.String getName() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getName();
}
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
一個data,一個reply抄囚,都是parcel對象霉赡。注意最核心的代碼:
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
mRemote其實(shí)就是proxy初始化的時(shí)候傳入進(jìn)來的binder對象。transact是什么方法呢怠苔?
/**
* Perform a generic operation with the object.
*
* @param code The action to perform. This should
* be a number between {@link #FIRST_CALL_TRANSACTION} and
* {@link #LAST_CALL_TRANSACTION}.
* @param data Marshalled data to send to the target. Must not be null.
* If you are not sending any data, you must create an empty Parcel
* that is given here.
* @param reply Marshalled data to be received from the target. May be
* null if you are not interested in the return value.
* @param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
*
* @return Returns the result from {@link Binder#onTransact}. A successful call
* generally returns true; false generally means the transaction code was not
* understood.
*/
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;
這里就從官方文檔注釋大概知道意思:
- 1.code表示要執(zhí)行什么動作同廉;
- 2.data和reply就是一個傳入值一個返回值;
- 3.flag只有0和flag_oneway柑司,0表示client需要等待server返回迫肖,而flag_oneway表示這是一個單項(xiàng)的活動,無需等待攒驰。所以參數(shù)為0時(shí)表示可能會有阻塞蟆湖,這里要多注意.
okay,讓我們再回到stub里面來玻粪,關(guān)注一下onTransact方法:
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_setName: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(descriptor);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
可以看到stub根據(jù)不同的code去執(zhí)行不同的方法隅津;
小結(jié)
通過以上的源碼分析能夠得出一次aidl的IPC(inter-process communication)大致流程:
- a.客戶端通過bindService綁定服務(wù)端的service
- b.拿到service之后拿到ibinder對象,再通過接口的Stub.asInterface將ibinder對象轉(zhuǎn)化為接口對象劲室。
- c.拿到接口對象之后調(diào)用接口的api伦仍,api里通過binder.transact方法調(diào)起遠(yuǎn)程通信,如果flag是0很洋,等待結(jié)果回來充蓝。
- d.遠(yuǎn)程接收到后,通過寫入?yún)?shù)到reply參數(shù)中去喉磁,然后再調(diào)用binder.transact方法返回谓苟。
這里再稍微提一下binder機(jī)制。因?yàn)檫@個實(shí)在是太重要了對于android系統(tǒng)來說协怒。
首先android系統(tǒng)不同進(jìn)程是不共享空間的涝焙,但是內(nèi)核空間是共享的,因此binder 驅(qū)動層肯定是在內(nèi)核空間的孕暇,相當(dāng)于一個底層中轉(zhuǎn)站的概念仑撞。后續(xù)將花大篇幅介紹binder。