android中AIDL機制分析

AIDL的使用

經過AIDL接口實現下面的功能:
在一個頁面中登陸,activity只負責接口調用和參數傳遞,具體實現由service完成,service執行登陸後把結果返回給activity。
首先須要新建一個.aidl文件,在裏面申明AIDL接口方法:java

interface IEcmServiceBinder {
     /** * 登陸TF卡 * 初始化加密卡而且獲取當前卡的狀態 * @param passwd 登陸卡的密碼 * @return 返回登陸結果: xxxxxxxxxx */
     int[] loginTFCard(String passwd);
}

寫好後build一下工程,而後在service端實現這個接口:android

public IEcmServiceBinder.Stub mEcmServiceBinder = new IEcmServiceBinder.Stub() {

        /** * 登陸TF卡 * 初始化加密卡而且獲取當前卡的狀態 * @param passwd 登陸卡的密碼 * @return 返回登陸結果: xxxxxxx */
        @Override
        public int[] loginTFCard(String passwd) throws RemoteException {
            return xxxxxxxxxxx;
        }
}

在activity端,採用bindservice方法啓動service,在onServiceConnected中拿到IEcmServiceBinder對象,就能夠調用service端的方法了:web

private ServiceConnection sc = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IEcmServiceBinder sEcmServiceAIDL = IEcmServiceBinder.Stub.asInterface(service);

            sEcmServiceAIDL.loginTFCard("123456");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

機制分析

先看看編譯.aidl文件後生成的IEcmServiceBinder.java,先看個大概,我把裏面的具體內容省略了:
這裏寫圖片描述
還有方法申明和方法對應的常量:
這裏寫圖片描述ide

這裏寫圖片描述
IEcmServiceBinder繼承了IInterface,這是一個interface:
這裏寫圖片描述
IEcmServiceBinder裏面有個Stub類,Stub裏還有個Proxy類。
咱們在service端實現登陸方法時是這樣寫的:svg

public IEcmServiceBinder.Stub mEcmServiceBinder = new IEcmServiceBinder.Stub() {
    ............
}

即在service端建立了一個Stub類對象,IEcmServiceBinder.Stub()是調用了Stub的構造函數:
這裏寫圖片描述
這是Binder中的方法:
這裏寫圖片描述
Stub原本就繼承了Binder,這樣是在這個Stub中保存了一個IInterface對象(就是這個Stub),將IInterface與Stub關聯起來,以後經過queryLocalInterface()能夠取出這個接口(Stub)。函數

下面看看activity端,onServiceConnected後,經過傳來的IBinder拿到AIDL對象:ui

IEcmServiceBinder sEcmServiceAIDL = IEcmServiceBinder.Stub.asInterface(service);

仍是要回到Stub類,它的asInterface()方法:
這裏寫圖片描述
以前提到,queryLocalInterface()會取出在構造函數中保存的Stub對象,若是這個對象是IEcmServiceBinder的實例,轉成IEcmServiceBinder返回;不然new一個Proxy對象返回。
這個地方如今不太明白,網上的說法:
這裏寫圖片描述
http://blog.csdn.net/c10wtiybq1ye3/article/details/78362429
怎麼區分不一樣進程的????????
關鍵在這幾行代碼:this

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.cetcs.encryptcardmanager.EcmService.IEcmServiceBinder))) {
return ((com.cetcs.encryptcardmanager.EcmService.IEcmServiceBinder)iin);
}
return new com.cetcs.encryptcardmanager.EcmService.IEcmServiceBinder.Stub.Proxy(obj);

傳了一個字符串常量進去,這個常量是完整類名,假如在其它進程中,能夠想象就是其它應用的包名+類名,以此來區分是否是在相同進程。
跟到代碼中去,是這樣的:加密

public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

mOwner是這樣定義的:spa

public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

這個方法又是在Stub的構造函數中,把descriptor傳進去的:

public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}

Stub,是在服務端建立的:
這裏寫圖片描述
這個stub是個靜態內部類,直接就構造出來了。在須要判斷是否是相同進程時,就經過stub的標籤來判斷是否是同樣的。
那麼又要看看Proxy的代碼了:
這裏寫圖片描述
還有一個與AIDL中方法名同樣的方法:
這裏寫圖片描述
先無論Proxy的其它代碼,注意圖中標紅的部分,調用了mRemote的transact方法,transact是Binder的方法:
這裏寫圖片描述
而後又調用Binder的onTransact:
這裏寫圖片描述
咱們看註釋,它須要被override,因此不用關內心面的實現。注意到mRemote是初始化傳進來的IBinder對象:
這裏寫圖片描述
最後一行,那個obj實際上是Stub構造函數中存入的Stub自己,這個Stub也override了onTransact方法:
這裏寫圖片描述
因此經過asInterface返回的這個Proxy,具備和service端同樣的方法loginTFCard,經過tracsact調用Stub中的onTransact,下面再看看onTransat:
這裏寫圖片描述
若是還有其它方法,這裏會有不少case。這裏傳進來了code,data,reply和flags。code就是每一個實現方法對應的一個int型常量,data保留了方法調用時傳如的參數,在這裏是密碼「123456」,reply中保留了方法調用的結果。這裏調用到了this.loginTFCard(_arg0),this就是Stub,是一開始在service端建立的:

public IEcmServiceBinder.Stub mEcmServiceBinder = new IEcmServiceBinder.Stub() {
    xxxxxxxxxxxxxxxxxxxxxxxxx
}

這樣回到了service端,能夠調用裏面的方法了。 因此,咱們經過IEcmServiceBinder.Stub.asInterface(service),拿到AIDL的代理類Proxy,Proxy經過transact,調用Stub的onTransact,間接調用到service端的代碼了。