AIDL是 Android Interface definition language的縮寫,即:Android接口定義語言掏熬。
進程隔離:不同進程間不可以相互訪問內(nèi)存空間,要想相互調(diào)用塞蹭,必須要進行進程間通信孽江。
本篇中不涉及Binder的底層原來,但是要理解一個知識點:客戶端進程持有BinderProxy類的對象番电,通過Binder驅(qū)動,向?qū)倪\行在服務端進程中的Binder對象發(fā)送消息(執(zhí)行遠程方法調(diào)用)辆琅∈欤可以類比java的Socket編程中的,Socket 向SocketServer發(fā)送消息的過程婉烟,不過Binder不僅僅是發(fā)送報文消息那么簡單娩井,他對遠程方法調(diào)用實現(xiàn)了封裝。
注:這不是一篇介紹如何使用AIDL的文章(如果想學習aidl如何使用移步官網(wǎng)案例似袁,任玉剛博客)洞辣,這篇文章主要解讀編譯系統(tǒng)根據(jù)aidl文件生成的java代碼,目的是為了將來讀懂ActivityManagerService的源碼昙衅。
AIDL生成代碼解析
為了便于閱讀生成的代碼扬霜,我們來寫一個最最簡單的AIDL,讓服務端為我們實現(xiàn)兩個數(shù)相加的功能而涉≈浚客戶端界面如下:
首先定義AIDL接口:
// ICalculate.aidl
package me.febsky.aidl;
interface ICalculate {
int add(int a, int b);
}
然后在AndroidStudio中運行Build-->Rebuild Project
或者點擊Gradle同步按鈕,這時候會在app-->build-->generated-->source-->aidl
下面生成ICalculate.java
這個類啼县。這些代碼是自動生成的材原,不可修改,應該說改了也不起作用季眷,下次編譯還會被覆蓋余蟹。
打開這個類文件,來看下生成的源碼(為了便于閱讀子刮,在Mac上可以按command+alt + L
來格式化代碼)威酒,現(xiàn)在摘錄代碼如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/liuqiang/Desktop/AIDL/app/src/main/aidl/me/febsky/aidl/ICalculate.aidl
*/
package me.febsky.aidl;
public interface ICalculate extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements me.febsky.aidl.ICalculate {
private static final java.lang.String DESCRIPTOR = "me.febsky.aidl.ICalculate";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an me.febsky.aidl.ICalculate interface,
* generating a proxy if needed.
*/
public static me.febsky.aidl.ICalculate asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof me.febsky.aidl.ICalculate))) {
return ((me.febsky.aidl.ICalculate) iin);
}
return new me.febsky.aidl.ICalculate.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_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements me.febsky.aidl.ICalculate {
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;
}
@Override
public int add(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_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int add(int a, int b) throws android.os.RemoteException;
}
好吧,怪不得Android要搞出AIDL這么個東西,我在aidl中就寫了一行代碼兼搏,系統(tǒng)生成了這么多卵慰,要不是系統(tǒng)自動生成佛呻,每次用到Binder和AIDL進行進程間通信的時候,都要手擼這么寫重復代碼鲤嫡。
這些代碼從何看起呢,為了便于從宏觀上觀察生成的代碼绑莺,我們折疊一下暫時不關(guān)心的代碼:
然后是這樣:
最后是這么個樣子:
從以上代碼來看,在生成的java文件中主要有三個類:ICalculate
诫肠,ICalculate.Stub
,ICalculate.Proxy
欺缘。其中Stub
是接口ICalculate
的靜態(tài)內(nèi)部類栋豫,Proxy
是Stub的私有靜態(tài)內(nèi)部類(個人認為其實Stub
和Proxy
沒有必要一定要做為ICalculate
的靜態(tài)內(nèi)部類谚殊,這樣放置只是為了便于管理和查看他們之間的關(guān)聯(lián)關(guān)系)
ICalculate
這個接口其實很簡單繼承與IInterface
,先不用管這個IInterface的作用嫩絮,只看ICalculate的話就是個普通的接口剿干,這里面有我們定義的add
方法,就是定了了我們要在AIDL中實現(xiàn)的業(yè)務邏輯怨愤。這個接口其實為了進程間通信,所有定義的是客戶端需要服務端提供的功能篮愉。
ICalculate.Stub
這個類很重要差导,它繼承了Binder類,實現(xiàn)了ICalculate接口颠蕴。從繼承關(guān)系來看他是一個具有ICalculate功能的Binder。好椅您,既然是一個Binder就具有了進程間通信的功能寡键。注意這個類是個抽象類,它只是定義了Binder的業(yè)務層通信功能员舵,但是具體的通信內(nèi)容(也就是我們的業(yè)務方法add方法)并沒有具體實現(xiàn),需要子類來實現(xiàn)藕畔。一般Stub的子類在服務端實現(xiàn)注服。
說到這里必須說下Binder,Binder是Android上比較復雜的一個東西了仍秤。但這里我們不分析Binder的通信原理可很。只需要知道凰浮,Binder和BinderProxy是成對出現(xiàn)的袜茧,客戶端進程持有BinderProxy對象,然后BinderProxy可以和binder驅(qū)動交互笛厦,binder驅(qū)動再去發(fā)消息給Binder對象從而實現(xiàn)IPC裳凸。
個人認為為了便于理解,完全可以把BinderProxy和Binder類比成javaTCP 中的Socket和SocketServer
看下Binder的源碼結(jié)構(gòu):
Binder和BinderProxy只是實現(xiàn)了進程間通信功能逗宁,具體通信內(nèi)容是啥他不關(guān)心瞎颗。通信內(nèi)容交給ICalculate.Stub.Proxy 和 ICalculate.Stub的子類來實現(xiàn)。
在ICalculate.Stub中有幾個很重要的方法:
- asInterface
- onTransact
首先看看asInterface方法引有,這個方法是一個靜態(tài)方法倦逐,我們在bind一個Service之后,在onServiceConnecttion的回調(diào)里面导帝,就是通過這個方法拿到一個遠程的service(這個Service不是Android的四大組件的那個Service)的代理(客戶端和服務端不在同一個進程中的情況下)穿铆,binderService時候的代碼如下:
ICalculate calculate;
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
calculate = ICalculate.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//一般情況下荞雏,如果是跨進程的穿件來的參數(shù)都是BinderProxy類型的
public static me.febsky.aidl.ICalculate asInterface(android.os.IBinder obj) {
//這種情況基本不存在,可以忽略悦陋,你傳了個null進來大家還玩啥筑辨?
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//這個if來判斷,客戶端和服務端是不是在一個進程中
//也就是來判斷暮现,傳進來的參數(shù)obj是Binder對象還是BinderProxy對象
//如果在同一個進程中傳入的是Binder對象楚昭,也即是Stub子類的對象抚太,以下if語句成立
if (((iin != null) && (iin instanceof me.febsky.aidl.ICalculate))) {
return ((me.febsky.aidl.ICalculate) iin);
}
return new me.febsky.aidl.ICalculate.Stub.Proxy(obj);
}
onTransact
后面我們和Proxy
中的transact
方法一塊分析。接下來先看Stub.Proxy
這個類电媳。
ICalculate.Stub.Proxy
從代碼中可以看出匆背,這個類的構(gòu)造方法接收一個IBinder的實現(xiàn)類身冀,其實這里主要是BinderProxy的對象括享。然后忽略其他铃辖,直接看我們的add方法猪叙。前面也提到了,客戶端通過Stub.asInterface 靜態(tài)方法犬第,持有Stub.Proxy 類的對象芒帕,然后和存在于服務端進程中Stub子類的對象進行通信。其實歸根到底是客戶端BinderProxy和服務端Binder的通信鉴分。
這個add方法可以解讀為带膀,Stub.Proxy 類的對象垛叨,持有BinderProxy的對象,通過BinderProxy對象舔株,像遠程的Binder對象發(fā)送消息还棱〔训龋看下發(fā)送消息的主要代碼辞做。可以先不用去管Parcel對象稚补,可以把它看做一個可以序列化的對象框喳,或者向遠程發(fā)送數(shù)據(jù)的載體厦坛。把要傳遞給遠程對象的參數(shù)放到Parcel中杜秸,然后調(diào)用BinderProxy的transact 方法润绎,發(fā)送消息到Stub子類對象中。
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
這個方法由三個參數(shù)
- Stub.TRANSACTION_add 方法名的唯一標示呢蛤,告訴遠程對象棍郎,我要執(zhí)行你的哪個方法坝撑,目前我們的接口中只定義了一個add方法,
- _data封裝了要調(diào)用的遠程方法的所有的需要的參數(shù)
- _reply 遠程方法返回值的載體
- flag 最后一個參數(shù)是個flag抚笔,默認0就可以了侨拦,好像是用來指定是不是單向的IPC的
通過以上過程,這樣一個遠程方法調(diào)用膨蛮,就會通過Binder機制季研,把消息(調(diào)用某個方法)發(fā)送到服務端進程中的相應對象中与涡。我們在此依然忽略BinderProxy和Binder之間跨進程通信的底層原理,只要知道氨肌,BinderProxy通過調(diào)用transact 方法酌畜,能通過Binder驅(qū)動桥胞,發(fā)消息到Binder進程就可以了考婴。繼續(xù)分析當BinderProxy通過transact 方法發(fā)送消息到服務端Binder子類對應的進程的時候井誉,Stub的子類是如何接收處理這個消息的颗圣,看Stub類的onTransact方法:
可以看到在這個方法中有個switch語句,這個code就是剛剛在transact中的第一參數(shù)奔则,用了標志該調(diào)用哪個方法易茬。其余方法也和transact 中的一一對應及老,不再解釋。其實上面的代碼也很好理解食铐,主要看第二個case里面語句嗎僧鲁,先把方法需要參數(shù)從data這個載體中讀出來寞秃,對應 BinderProxy transact方法的寫入操作,然后調(diào)用真正的業(yè)務方法addint _result = this.add(_arg0, _arg1);
并把返回值寫入到reply 這個返回值載體中從而能把方法返回值傳遞個客戶端朗涩。從上面可以看到Stub是個抽象類绑改,并沒有實現(xiàn)業(yè)務方法add绢淀,這個要在他的子類中實現(xiàn)瘾腰,具體代碼如下:
//注意這個Service要放到單獨的進程中運行
public class CalculateService extends Service {
private ICalculate.Stub calculate = new ICalculate.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
return calculate;
}
}
【[測試代碼下載]
(http://download.csdn.net/download/niyingxunzong/9977048)】
測試效果圖:
重要知識點
在使用AIDL的時候蹋盆,編譯工具會給我們生成一個Stub的靜態(tài)內(nèi)部類;這個類繼承了Binder, 說明它是一個Binder本地對象楞抡,它實現(xiàn)了IInterface接口召廷,表明它具有遠程Server承諾給Client的能力;Stub是一個抽象類先紫,具體的IInterface的相關(guān)實現(xiàn)需要我們手動完成筹煮,這里使用了策略模式。
其中基本的UML類圖如下本冲,類圖中并沒有標注出所有的方法檬洞,只是標注了我們關(guān)心的幾個: