IPC:Inter Process Communication,跨進(jìn)程間通信胶惰。
AIDL:Android Interface Definition Language傻工,Android接口定義語(yǔ)言,用于生成可以在Android設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信的代碼孵滞。
Binder:是Android中一種跨進(jìn)程通信的方式中捆。從Android FrameWork角度來(lái)說(shuō),Binder是ServiceManager連接各種Manager和相應(yīng)ManagerService的橋梁坊饶。從Android應(yīng)用層的角度來(lái)說(shuō)泄伪, BInder是客戶端和服務(wù)端進(jìn)行通信的媒介。
三者的關(guān)系:
AIDL是基于Binder機(jī)制實(shí)現(xiàn)Android上的IPC匿级。
一蟋滴、AIDL實(shí)踐
先從例子出發(fā),從例子理解aidl痘绎。AIDL涉及一個(gè)服務(wù)端和一個(gè)客戶端津函。
1、服務(wù)端
(1)新建一個(gè)工程孤页,AIDLServer尔苦。
(2)右鍵新建一個(gè)AIDL File,刪掉里面的方法,寫(xiě)上自己的方法聲明蕉堰。
// IMyAidlInterface.aidl
package com.cm.aidlserver;
interface IMyAidlInterface {
void callF1();
int callF2();
}
(3)選擇Build -> Rebuild Project,會(huì)自動(dòng)根據(jù)接口生成Java文件悲龟。位置在app/build/generated/source/aidl下面屋讶。可以先大概看一下生成的內(nèi)容须教,本質(zhì)還是一個(gè)繼承了接口的接口皿渗。里面的內(nèi)容等下解釋。
(4)上面生成的只是個(gè)接口轻腺,是必須要實(shí)現(xiàn)的乐疆。
新建一個(gè)服務(wù)service。
package com.cm.aidlserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return myBinder;
}
private IBinder myBinder = new IMyAidlInterface.Stub() {
@Override
public void callF1() throws RemoteException {
Log.i("chenming server", "callerF1");
}
@Override
public int callF2() throws RemoteException {
Log.i("chenming server", "callerF2");
return 0;
}
};
}
在onBind方法中返回對(duì)應(yīng)的binder對(duì)象贬养,binder對(duì)象為IMyAidlInterface.Stub的實(shí)例對(duì)象挤土,實(shí)現(xiàn)了其中的抽象方法,即我們定義的aidl中聲明的方法误算。
為了可以在客戶端調(diào)用仰美,需要在xml中將外界調(diào)用設(shè)置為true。
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
2儿礼、客戶端
(1)新建一個(gè)工程咖杂,AIDLClient。
(2)將服務(wù)器的AIDL文件復(fù)制到該工程中蚊夫,選擇Build -> Rebuild Project诉字,會(huì)自動(dòng)根據(jù)接口生成和服務(wù)端一樣的Java接口文件。
(3)為了可以調(diào)用服務(wù)端的方法知纷,需要通過(guò)binder進(jìn)行調(diào)用壤圃,因此在客戶端中要定義該binder。
IMyAidlInterface iMyAidlInterface;
(4)然后綁定服務(wù)端的提供的服務(wù)屈扎。
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.cm.aidlserver", "com.cm.aidlserver.MyService"));
bindService(intent, conn, BIND_AUTO_CREATE);
(5)綁定成功之后會(huì)回調(diào)對(duì)應(yīng)的方法埃唯,在方法中初始化binder。
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
iMyAidlInterface = null;
}
};
通過(guò)IMyAidlInterface.Stub.asInterface(iBinder)初始化binder鹰晨。
(6)調(diào)用對(duì)應(yīng)的方法墨叛,以調(diào)用callF1方法為例,調(diào)用如下模蜡。
private void callF1() {
try {
iMyAidlInterface.callF1();
} catch (RemoteException e) {
e.printStackTrace();
}
}
從log可以看出漠趁,可以通過(guò)客戶端調(diào)用服務(wù)端的方法。
注意:原生系統(tǒng)中忍疾,可以通過(guò)這個(gè)方法進(jìn)行贝炒活,即當(dāng)Client端啟動(dòng)的時(shí)候卤妒,會(huì)通過(guò)binder service拉起Server端甥绿,但是由于國(guó)產(chǎn)手機(jī)的系統(tǒng)都經(jīng)過(guò)了優(yōu)化字币,會(huì)攔截關(guān)聯(lián)啟動(dòng),解決方法有兩個(gè)共缕,一個(gè)是先啟動(dòng)server再用client去調(diào)用洗出,另一個(gè)是打開(kāi)手機(jī)里面的關(guān)聯(lián)啟動(dòng)允許即可(以華為手機(jī)為例,打開(kāi)手機(jī)管家->啟動(dòng)管理图谷,找到AIDLServer改成手動(dòng)管理翩活,允許關(guān)聯(lián)啟動(dòng))。
二便贵、AIDL原理分析
客戶端怎么跨進(jìn)程調(diào)用server端的方法的呢菠镇?
這個(gè)需要從AIDL自動(dòng)生成的Java類(lèi)分析。
查看生成的Java類(lèi)IMyAidlInterface.java承璃,接下來(lái)一行行解讀利耍。
首先,該類(lèi)是一個(gè)繼承自接口的接口盔粹,從上面實(shí)現(xiàn)可以知道具體的是實(shí)現(xiàn)是在service中堂竟。
然后,定義了一個(gè)抽象類(lèi)Stub玻佩,這個(gè)類(lèi)本質(zhì)就是一個(gè)binder出嘹,這個(gè)類(lèi)里面有以下的成員與方法:
(1)DESCRIPTOR成員,是Binder的唯一標(biāo)識(shí)
(2)asInterface方法
傳入?yún)?shù)為一個(gè)Binder咬崔,返回一個(gè)IMyAidlInterface税稼,是用于將服務(wù)端的Binder對(duì)象轉(zhuǎn)換成客戶端所需的AIDL接口類(lèi)型的對(duì)象。
里面先調(diào)用queryLocalInterface方法垮斯,這個(gè)方法可以從源碼里面看見(jiàn)當(dāng)客戶端和服務(wù)端為同一個(gè)進(jìn)程的時(shí)候郎仆,返回服務(wù)端的AIDL接口類(lèi)型的對(duì)象,不同進(jìn)程的時(shí)候返回null兜蠕。所以根據(jù)iin進(jìn)行判斷扰肌,如果返回不為null,說(shuō)明在同一個(gè)進(jìn)程內(nèi)調(diào)用熊杨,直接返回AIDL接口類(lèi)型的對(duì)象曙旭,為null的時(shí)候說(shuō)明不在同一個(gè)進(jìn)程內(nèi)調(diào)用,返回封裝后的對(duì)象Stub.Proxy晶府。
(3)asBinder方法桂躏,返回當(dāng)前的binder對(duì)象。
(4)onTransact方法川陆。當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求的時(shí)候剂习,遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層封裝后交到該方法處理。原型如下:
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
code為請(qǐng)求參數(shù),根據(jù)這個(gè)參數(shù)來(lái)確定調(diào)用哪個(gè)方法鳞绕,data用以傳遞方法參數(shù)失仁,reply用以傳遞結(jié)果即返回值。該方法的返回值標(biāo)記是否調(diào)用成功们何。
(5)對(duì)于每個(gè)方法定義一個(gè)code陶因。
static final int TRANSACTION_callF1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_callF2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
(6)從上面知道,如果是同一個(gè)進(jìn)程調(diào)用的是Stub的方法垂蜗,如果不同進(jìn)程,調(diào)用的是Stub.Proxy里面的方法解幽,以其中一個(gè)方法Stub.Proxy的callF1方法為例贴见,分析如下。
首先創(chuàng)建輸入?yún)?shù)_data和輸出參數(shù)_reply躲株,然后將該方法的參數(shù)寫(xiě)入_data片部,然后調(diào)用transact方法發(fā)送RPC(遠(yuǎn)程過(guò)程調(diào)用),同時(shí)線程掛起霜定,然后服務(wù)端的onTransact會(huì)被調(diào)用档悠,等到結(jié)果返回之后繼續(xù)執(zhí)行,將結(jié)果寫(xiě)入_reply中望浩。
可以看見(jiàn)辖所,AIDL是基于Binder機(jī)制實(shí)現(xiàn)的,通過(guò)AIDL自動(dòng)生成的接口里面的Binder的子類(lèi)實(shí)現(xiàn)遠(yuǎn)程接口調(diào)用磨德,整個(gè)流程如下圖所示缘回。
注意:由于客戶端在通過(guò)binder調(diào)用服務(wù)端的時(shí)候,會(huì)把線程掛起典挑,因此酥宴,一般需要另開(kāi)線程執(zhí)行,如果在主線程執(zhí)行您觉,如果這個(gè)方法是耗時(shí)方法拙寡,會(huì)導(dǎo)致ANR情況。
三琳水、深入理解Binder
上面從主要從應(yīng)用層的角度分析AIDL肆糕,可以看出AIDL底層是基于Binder實(shí)現(xiàn)的,那么Binder底層原理是怎么樣的呢在孝?
首先擎宝,先理解進(jìn)程內(nèi)存空間的關(guān)系,如下圖所示浑玛。
可以看見(jiàn)雖然不同進(jìn)程的用戶空間是非共享的绍申,但是內(nèi)核空間是共享的,Client進(jìn)程向Server進(jìn)程通信,恰恰是利用進(jìn)程間可共享的內(nèi)核內(nèi)存空間來(lái)完成底層通信工作的极阅。
Binder通信采用C/S架構(gòu)胃碾,基本架構(gòu)圖如下圖所示。
Service Manager用于管理系統(tǒng)中的各種服務(wù)筋搏。圖中Client/Server/ServiceManage之間的相互通信都是基于Binder機(jī)制仆百,所以三個(gè)步驟都是C/S架構(gòu)。
首先奔脐,注冊(cè)服務(wù)俄周,這個(gè)過(guò)程Server是客戶端,ServiceManager是服務(wù)端髓迎。
然后峦朗,Client端進(jìn)行服務(wù)獲取,這個(gè)過(guò)程Client是客戶端排龄,ServiceManager是服務(wù)端波势。
最后,使用服務(wù)橄维,Client根據(jù)得到的Service信息建立與Service所在的Server進(jìn)程通信的通路尺铣,然后就可以直接與Service交互。該過(guò)程:client是客戶端争舞,server是服務(wù)端凛忿。
四、例子回顧
首先竞川,啟動(dòng)AIDLServer應(yīng)用后侄非,MyService啟動(dòng),向Service Manager注冊(cè)流译。然后啟動(dòng)AIDLClient通過(guò)綁定服務(wù)獲取對(duì)應(yīng)的服務(wù)逞怨,服務(wù)連接成功之后,調(diào)用IMyAidlInterface.Stub.asInterface獲取客戶端所需的AIDL接口類(lèi)型的對(duì)象福澡,由于是不同的進(jìn)程叠赦,因此會(huì)獲取到對(duì)應(yīng)的代理類(lèi)。調(diào)用方法callF1方法的時(shí)候革砸,會(huì)去調(diào)用對(duì)應(yīng)service里面真正實(shí)現(xiàn)的callF1()除秀。