轉(zhuǎn)載請注明文章出處LooperJing!
看了幾天的Binder,決定有必要寫一篇博客,記錄一下學(xué)習(xí)成果,Binder是Android中比較綜合的一塊知識了,目前的理解只限于JAVA層。首先Binder是干嘛用的?不用說,跨進程通信全靠它,操作系統(tǒng)的不同進程之間,數(shù)據(jù)不共享,對于每個進程來說,它都天真地以為自己獨享了整個系統(tǒng),完全不知道其他進程的存在,進程之間需要通信需要某種系統(tǒng)機制才能完成,在Android整個系統(tǒng)架構(gòu)中,采用了大量的C/S架構(gòu)的思想,所以Binder的作用就顯得非常重要了,但是這種機制為什么是Binder呢?在Linux中的RPC方式有管道,消息隊列,共享內(nèi)存等,消息隊列和管道采用存儲-轉(zhuǎn)發(fā)方式,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,然后再從內(nèi)核緩存區(qū)拷貝到接收方緩存區(qū),這樣就有兩次拷貝過程。共享內(nèi)存不需要拷貝,但控制復(fù)雜,難以使用。Binder是個折中的方案,只需要拷貝一次就行了。其次Binder的安全性比較好,好在哪里,在下還不是很清楚,基于安全性和傳輸?shù)男士紤],選擇了Binder。Binder的英文意思是粘結(jié)劑,Binder對象是一個可以跨進程引用的對象,它的實體位于一個進程中,這個進程一般是Server端,該對象提供了一套方法用以實現(xiàn)對服務(wù)的請求,而它的引用卻遍布于系統(tǒng)的各個進程(Client端)之中,這樣Client通過Binder的引用訪問Server,所以說,Binder就像膠水一樣,把系統(tǒng)各個進程粘結(jié)在一起了,廢話確實有點多。
一、概念理解
為了從而保障了系統(tǒng)的安全和穩(wěn)定,整個系統(tǒng)被劃分成內(nèi)核空間和用戶空間
內(nèi)核空間:獨立于普通的應(yīng)用程序,可以訪問受保護的內(nèi)存空間,有訪問底層硬件設(shè)備的所有權(quán)限。
用戶空間:相對與內(nèi)核空間,上層運用程序所運行的空間就是用戶空間,用戶空間訪問內(nèi)核空間的唯一方式就是系統(tǒng)調(diào)用。一個4G的虛擬地址空間,其中3G是用戶空間,剩余的1G是內(nèi)核空間。如果一個用戶空間想與另外一個用戶空間進行通信,就需要內(nèi)核模塊支持,這個運行在內(nèi)核空間的,負(fù)責(zé)各個用戶進程通過Binder通信的內(nèi)核模塊叫做Binder驅(qū)動,雖然叫做Binder驅(qū)動,但是和硬件并沒有什么關(guān)系,只是實現(xiàn)方式和設(shè)備驅(qū)動程序是一樣的,提供了一些標(biāo)準(zhǔn)文件操作。
二、Binder的通信模型
在寫AIDL的時候,一般情況下,我們有兩個進程,一個作為Server端提供某種服務(wù),然后另外一個進程作為Client端,連接Server端之后,就 可以使用Server里面定義的服務(wù)。這種思想是一種典型的C/S的思想。值得注意的是Android系統(tǒng)中的Binder自身也是C/S的架構(gòu),也有Server端與Client端。一個大的C/S架構(gòu)中,也有一個小的C/S架構(gòu)。
先籠統(tǒng)的說一下,在整個Binder框架中,由系列組件組成,分別是Client、Server、ServiceManager和Binder驅(qū)動程序,其中Client、Server和ServiceManager運行在用戶空間,Binder驅(qū)動程序運行內(nèi)核空間。運行在用戶空間中的Client、Server和ServiceManager,是在三個不同進程中的,Server進程中中定義了服務(wù)提供給Client進程使用,并且Server中有一個Binder實體,但是Server中定義的服務(wù)并不能直接被Client使用,它需要向ServiceManager注冊,然后Client要用服務(wù)的時候,直接向ServiceManager要,ServiceManager返回一個Binder的替身(引用)給Client,這樣Client就可以調(diào)用Server中的服務(wù)了。
場景:進程A要調(diào)用進程B里面的一個draw方法處理圖片。
分析:在這種場景下,進程A作為Client端,進程B做為Server端,但是A/B不在同一個進程中,怎么來調(diào)用B進程的draw方法呢,首先進程B作為Server端創(chuàng)建了Binder實體,為其取一個字符形式,可讀易記的名字,并將這個Binder連同名字以數(shù)據(jù)包的形式通過Binder驅(qū)動發(fā)送給ServiceManager,也就是向ServiceManager注冊的過程,告訴ServiceManager,我是進程B,擁有圖像處理的功能,ServiceManager從數(shù)據(jù)包中取出名字和引用以一個注冊表的形式保留了Server進程的注冊信息。為什么是以數(shù)據(jù)包的形式呢,因為這是兩個進程,直接傳遞對象是不行滴,只能是一些描述信息。現(xiàn)在Client端進程A聯(lián)系ServiceManager,說現(xiàn)在我需要進程B中圖像處理的功能,ServiceManager從注冊表中查到了這個Binder實體,但是呢,它并不是直接把這個Binder實體直接給Client,而是給了一個Binder實體的代理,或者說是引用,Client通過Binder的引用訪問Server。分析到現(xiàn)在,有個關(guān)鍵的問題需要說一下,ServiceManager是一個進程,Server是另一個進程,Server向ServiceManager注冊Binder必然會涉及進程間通信。當(dāng)前實現(xiàn)的是進程間通信卻又要用到進程間通信,這就好象蛋可以孵出雞前提卻是要找只雞來孵蛋,確實是這樣的,ServiceManager中預(yù)先有了一個自己的Binder對象(實體),就是那只雞,然后Server有個Binder對象的引用,就是那個蛋,Server需要通過這個Binder的引用來實現(xiàn)Binder的注冊。雞就一只,蛋有很多,ServiceManager進程的Binder對象(實體)僅有一個,其他進程所擁有的全部都是它的代理。同樣一個Server端Binder實體也應(yīng)該只有一個,對應(yīng)所有Client端全部都是它的代理。
我們再次理解一下Binder是什么?在Binder通信模型的四個角色里面;他們的代表都是“Binder”,一個Binder對象就代表了所有,包括了Server,Client,ServiceManager,這樣,對于Binder通信的使用者而言,不用關(guān)心實現(xiàn)的細(xì)節(jié)。對Server來說,Binder指的是Binder實體,或者說是本地對象,對于Client來說,Binder指的是Binder代理對象,也就是Binder的引用。對于Binder驅(qū)動而言,在Binder對象進行跨進程傳遞的時候,Binder驅(qū)動會自動完成這兩種類型的轉(zhuǎn)換。
簡單的總結(jié)一下,通過上面一大段的分析,一個Server在使用的時候需要經(jīng)歷三個階段
- 服務(wù)注冊 Server端與SystemManager之間的IPC
- 服務(wù)檢索 Client端與SystemManager之間的IPC
- 服務(wù)使用 Client端與Server端之間的IPC
三、AIDL過程分析
1、定義一個AIDL文件
Game.aidl
package test.wangjing.com.aidl.domain;
parcelable Game;
GameManager .aidl
package test.wangjing.com.aidl;
import test.wangjing.com.aidl.domain.Game;
interface GameManager {
Game querryGameById(int pGameId);
}
2、定義遠(yuǎn)端服務(wù)Service
在遠(yuǎn)程服務(wù)中的onBind方法,實現(xiàn)AIDL接口的具體方法,并且返回Binder對象
@Override
public IBinder onBind(Intent intent) {
return mGameManager;
}
GameManager.Stub mGameManager = new GameManager.Stub() {
@Override
public IBinder asBinder() {
return null;
}
@Override
public Game querryGameById(int pGameId) throws RemoteException {
return mGameMap.get(pGameId);
}
};
3、本地創(chuàng)建連接對象
private class GameServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mGameManager = (GameManager.Stub) GameManager.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
public void actionService(View view) {
Intent intent = new Intent(this, GameService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
以上就是一個遠(yuǎn)端服務(wù)的一般套路,如果是在兩個進程中,就可以進程通信了,現(xiàn)在我們分析一下,這個通信的流程。重點是GameManager這個編譯生成的類。
public interface GameManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements test.wangjing.com.aidl.GameManager {
private static final java.lang.String DESCRIPTOR = "test.wangjing.com.aidl.GameManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an test.wangjing.com.aidl.GameManager interface,
* generating a proxy if needed.
*/
public static test.wangjing.com.aidl.GameManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof test.wangjing.com.aidl.GameManager))) {
return ((test.wangjing.com.aidl.GameManager) iin);
}
return new test.wangjing.com.aidl.GameManager.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_querryGameById: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
test.wangjing.com.aidl.domain.Game _result = this.querryGameById(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements test.wangjing.com.aidl.GameManager {
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 test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
test.wangjing.com.aidl.domain.Game _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(pGameId);
mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = test.wangjing.com.aidl.domain.Game.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_querryGameById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException;
}
從類的關(guān)系來看,首先接口GameManager 繼承 IInterface ,IInterface是一個接口,在GameManager內(nèi)部有一個內(nèi)部類Stub,Stub繼承了Binder,(Binder實現(xiàn)了IBinder),并且實現(xiàn)了GameManager接口,在Stub中還有一個內(nèi)部類Proxy,Proxy也實現(xiàn)了GameManager接口,一個整體的結(jié)構(gòu)是這樣的
現(xiàn)在的問題是,Stub是什么?Proxy又是什么?在上面說了在Binder通信模型的四個角色里面;他們的代表都是“Binder”,一個Binder對象就代表了所有,包括了Server,Clinet,ServiceManager,為了兩個進程的通信,系統(tǒng)給予的內(nèi)核支持是Binder,在抽象一點的說,Binder是系統(tǒng)開辟的一塊內(nèi)存空間,兩個進程往這塊空間里面讀寫數(shù)據(jù)就行了,Stub從Binder中讀數(shù)據(jù),Proxy向Binder中寫數(shù)據(jù),達(dá)到進程間通信的目的。首先我們分析Stub。
public static abstract class Stub extends android.os.Binder implements test.wangjing.com.aidl.GameManager
Stub 類繼承了Binder ,說明了Stub有了跨進程傳輸?shù)哪芰Γ瑢崿F(xiàn)了GameManager接口,說明它有了根據(jù)游戲ID查詢一個游戲的能力。我們在bind一個Service之后,在onServiceConnecttion的回調(diào)里面,就是通過asInterface方法拿到一個遠(yuǎn)程的service的。
public static test.wangjing.com.aidl.GameManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof test.wangjing.com.aidl.GameManager))) {
return ((test.wangjing.com.aidl.GameManager) iin);
}
return new test.wangjing.com.aidl.GameManager.Stub.Proxy(obj);
}
asInterface調(diào)用queryLocalInterface。
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
mDescriptor,mOwner其實是Binder的成員變量,Stub繼承了Binder,在構(gòu)造函數(shù)的時候,對著兩個變量賦的值。
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
如果客戶端和服務(wù)端是在一個進程中,那么其實queryLocalInterface獲取的就是Stub對象,如果不在一個進程queryLocalInterface查詢的對象肯定為null,因為不同進程有不同虛擬機,肯定查不到mOwner對象的,所以這時候其實是返回的Proxy對象了。拿到Stub對象后,通常在onServiceConnected中,就把這個對象轉(zhuǎn)換成我們多定義AIDL接口。
private class GameServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mGameManager = (GameManager.Stub) GameManager.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
比如我們這里會轉(zhuǎn)換成GameManager,有了GameManager對象,就可以調(diào)用后querryGameById方法了。如果是一個進程,那直接調(diào)用的是自己的querryGameById方法,如果不是一個進程,那調(diào)用了就是代理的querryGameById方法了。
private static class Proxy implements test.wangjing.com.aidl.GameManager {
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 test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
test.wangjing.com.aidl.domain.Game _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(pGameId);
mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = test.wangjing.com.aidl.domain.Game.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
看到其中關(guān)鍵的一行是
mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);
mRemote就是一個IBinder對象,相對于Stub,Proxy 是組合關(guān)系(HAS-A),內(nèi)部有一個IBinder對象mRemote,Stub是繼承關(guān)系(IS-A),直接實現(xiàn)了IBinder接口。
public native boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
transact是個native方法,最終還會回掉JAVA層的onTransact方法。
@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_querryGameById: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
test.wangjing.com.aidl.domain.Game _result = this.querryGameById(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact根據(jù)調(diào)用號(每個AIDL函數(shù)都有一個編號,在跨進程的時候,不會傳遞函數(shù),而是傳遞編號指明調(diào)用哪個函數(shù))調(diào)用相關(guān)函數(shù);在這個例子里面,調(diào)用了Binder本地對象的querryGameById方法;這個方法將結(jié)果返回給驅(qū)動,驅(qū)動喚醒掛起的Client進程里面的線程并將結(jié)果返回。于是一次跨進程調(diào)用就完成了。
***Please accept mybest wishes for your happiness and success ! ***