Binder淺析
1. 背景知識
Binder在Android系統中是用來進行進程間通信的,所以在介紹Binder之前需要首先明確進程之間的通信,再次之前需要明確一些概念。
進程可以說是操作系統中最為重要的抽象概念,操作系統為了確保進程之間不會產生相互干擾,同時為了便于編寫程序,采用了進程隔離的機制,即為每個進程分配獨立虛擬地址空間,進程之間感覺不到彼此的存在,感覺自己仿佛占用整個內存空間。這樣保證了進程的數據安全,也使得編程更加方便,但是必然存在另外的問題,那就是進程間通信,進程不可能完全獨立運行,有時候需要相互通信獲取別的進程的運行結果等,因此需要想辦法解決進程間通信的問題。
操作系統除了為負責管理和調度進程之外,還需要管理計算機的硬件資源,但是不同進程對硬件資源的使用權限需要得到控制,因此操作系統區分了內核空間和用戶空間。部分計算機資源由內核空間的進程負責管理,用戶空間的進程無法訪問,這種隔離可以起到安全作用。但是用戶空間的進程有時候是需要訪問一些計算機資源的,這時操作系統的內核就提供系統調用,可以在用戶空間中使用,于是就出現了內核態和用戶態的概念。用戶進程運行在用戶空間,此時處于用戶態,沒有特殊權限,不會對計算機資源造成威脅,如果需要部分的計算機資源可以通過系統調用使得該用戶進程陷入到內核態,運行在內核空間,由內核控制訪問計算機資源。
明白了系統調用和用戶空間,內核空間就能夠想到如何進行進程間通信了,既然用戶空間的進程之間相互隔離,感覺不到彼此的存在,那么就可以通過操作系統的內核進行進程間通信了。Unix或者Linux系統的傳統進程間通信機制比如socket,共享內存,信號量等都是基于內核實現的。Android是基于Linux的操作系統,而Linux內核是可以動態加載內核模塊的,即在運行時鏈接到內核作為內核的一部分運行于內核空間,所以Android系統通過添加一個內核模塊,用戶空間的進程都是通過這個模塊作為橋梁進行進程間通信,這里就出現了第一個與Binder有關的概念,即Binder驅動。
驅動程序一般指的是設備驅動程序(Device Driver),是一種可以使計算機和設備通信的特殊程序。相當于硬件的接口,操作系統只有通過這個接口,才能控制硬件設備的工作;
這里Binder驅動的個人理解是,它作為一個內核模塊,管理一塊內存,運行在內核空間,并提供進程間通信的功能,如果將這塊內存看做是一個硬件的話,這個內核模塊就是一個驅動程序,所以稱之為Binder驅動。由于水平有限,本文只是對Binder通信機制的應用層次做淺顯分析,因此對Binder驅動并不做了解。
2.Binder通信模型
在介紹Binder通信模型之前有一個問題那就是Android為什么選擇使用Binder,而不是之前Linux系統中就存在的傳統進程間通信機制,網上的資料說是考慮到安全和性能兩個方面,由于對Linux系統了解很少,對Socket,共享內存等知之甚少,而且對Binder理解也十分淺顯,所以對于Binder在安全和性能兩個方面的提升并不理解,這部分需要進一步學習。
Binder通信模型采用了C/S架構,即Client和Server之間通信。在這里需要首先明確Client和Server的概念,他們代表了兩種身份,某個進程在進行進程間通信時并不是一直扮演一種身份,而是根據一次進程間通信中,進程是請求某項功能還是提供某種功能扮演著不同身份。此外,這里還有另外兩個概念,就是ServiceManager(以下簡稱SM)和Service,SM是一個守護進程,在Binder通信模型中作為中心的調度者,而Service則是代表Server為Client提供的一項服務,可以理解為一些功能的集合。
所以Binder通信模型可以簡單總結為以下的過程,Server向SM注冊,它可以提供哪些Service,Client在需要某些功能時向SM發出請求,SM通過查詢找到相應Server,此時通過Binder驅動在Client與Server之間建立連接,這樣Client與Server就實現了通信。這里需要注意的是,注冊和請求過程也是通過Binder通信完成的。也就是說Server在注冊的時候它是Client,SM是Server,請求過程同理,在建立連接以后,Server才作為Server身份,Client依然作為Client使用Server提供的Service。
Binder的通信模型很簡單,網上的流程簡介圖也很多,這里就不再貼出來了,由于采用的是C/S架構,那么與計算機網絡的應用層必然十分相似。Client和Server很簡單,類似于瀏覽器和服務器,SM則類似于DNS,不過也有一些區別,DNS是負責根據域名查找IP地址,然后傳輸到相應的服務器則是交由網絡層處理,而這里的SM則是根據請求的Service查找到相應的Server,然后則是由Binder驅動負責在Client與Server之間建立連接。最后Service的類比概念也很簡單,就像是HTTP,FTP等,也就是Server所能提供的服務。
3. Binder通信原理
上一部分介紹Binder的通信模型中說SM負責在Client與Server之間建立連接,底層由Binder驅動負責,但是在應用層如何建立連接依然不清楚,由于本文是淺析Binder,所以對Binder驅動不做分析,只是對應用層的內容做出分析。
我們都知道Android操作系統是基于Linux內核,底層是C語言實現,是面向過程的語言,而FrameWork層以及應用層則是基于Java語言,Java語言最大的特點就是面向對象的語言,那么在應用層使用進程間通信,如果在進程間傳遞對象則是最好的方法。那么這里就出現了第二個與Binder相關的概念,即Binder對象,即可以在進程間傳遞的對象。但是之前說進程間通信底層是由Binder驅動負責,Binder驅動是運行在內核空間的內核模塊,那么肯定是由C語言實現的,并不支持對象的傳遞,除非是可以序列化的對象,那么如果在應用層中屏蔽底層細節,建立一種可以傳遞對象的假象呢,那么就是使用代理,就是使用BinderProxy。
Binder對象是Server的本地對象,可以提供某項功能或者說可以作為某個Service,當Client需要某個服務向SM查詢時,SM找到對應的Server,由Binder驅動向Client進程提供這個Binder對應的BinderProxy,Client進程可以拿到BinderProxy對象,就像是一個對Binder對象的引用,仿佛Binder對象從Server進程傳遞到了Client進程。其實Binder本地對象只有一個,而有很多的代理處在不同的Client進程中,Client進程通過調用BinderProxy方法就可以使用Server在Binder中實現的功能,中間的傳遞則是由Binder驅動完成,所以Binder驅動只需要負責Binder與BinderProxy之間的通信即可,這樣Binder看起來就像是可以傳遞的對象。不過這里有一個問題,就是有很多BinderProxy的情況下,如果做同步與互斥,還需要進一步學習。
4. AIDL分析
上面概念扯了一堆,該分析一點實際的代碼了,即在Android開發過程中進程間通信通常使用的AIDL語言。但是在介紹使用AIDL之前還是需要啰嗦幾個概念,即在Java中的IBinder, Binder, BinderProxy, IInterface, Stub, StubProxy等幾個涉及到的類。
首先是第一個接口IBinder, 它代表了一種跨進程傳輸的能力;只要實現了這個接口,就能將這個對象進行跨進程傳遞;這是驅動底層支持的;在跨進程數據流經驅動的時候,驅動會識別IBinder類型的數據,從而自動完成不同進程Binder本地對象以及Binder代理對象的轉換。
Java層的Binder類,代表的其實就是Binder本地對象。BinderProxy類是Binder類的一個內部類,它代表遠程進程的Binder對象的本地代理;這兩個類都繼承自IBinder, 因而都具有跨進程傳輸的能力;實際上,在跨越進程的時候,Binder驅動會自動完成這兩個對象的轉換。
然后第二個接口IInterface,代表Server所提供的功能或者說是服務。在Server中定義的功能接口都需要擴展這個接口。
最后是Stub和Stub.Proxy。Stub是在AIDL中自動生成的類,它的字面意思是存根,它繼承自Binder,同時實現了IInterface的接口,有點使用的適配器模式的意思,即代表了可以在進程間傳遞的對象,同時又可以提供對應的功能。而Stub.Proxy則是Stub的一個內部類,雖然看名字像是Stub的代理,其實這里并沒有使用代理模式,Stub.Proxy中,個人理解也有點使用適配器模式的意思,而它并沒有繼承, 而是使用了組合的方式,即持有一個BinderProxy, 因為是BinderProxy,所以名字是StubProxy,而不是只它是Stub的代理。這兩個類在這里說過于空虛,在后面的AIDL的生成代碼中再做詳細介紹。
首先是定義自己的AIDL文件
interface IMyAidlInterface {
void myMethod();
}
然后在生成的文件中找到IMyAidlInterface對應的Java文件,本文主要是對生成的文件進行分析,從而理解Binder通信機制。
下面的代碼中是對生成的代碼做了簡化,便于閱讀
public interface IMyAidlInterface extends IInterface{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends Binder implements IMyAidlInterface{
private static final java.lang.String DESCRIPTOR = "com.example.androidlearning.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.androidlearning.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static IMyAidlInterface asInterface(IBinder obj){
if ((obj==null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof IMyAidlInterface))) {
return ((IMyAidlInterface)iin);
}
return new Stub.Proxy(obj);
}
@Override public IBinder asBinder(){
return this;
}
@Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException{
switch (code){
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_myMethod:
{
data.enforceInterface(DESCRIPTOR);
this.myMethod();
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IMyAidlInterface{
private IBinder mRemote;
Proxy(IBinder remote){
mRemote = remote;
}
@Override public IBinder asBinder(){
return mRemote;
}
public String getInterfaceDescriptor(){
return DESCRIPTOR;
}
@Override public void myMethod() throws RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_myMethod, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_myMethod = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void myMethod() throws android.os.RemoteException;
}
這里首先看一下生成代碼的主體結構,IMyAidlInterface擴展IInterface,而其內部類Stub繼承Binder,同時實現IMyAidlInterface,并且Stub是一個抽象類,我們所定義的功能方法作為抽象方法,留由子類實現。Stub的一個子類Stub.Proxy則是使用另一種適配器模式的方法,即組合方式包含BinderProxy, 同時實現IMyAidlInterface, 而其他具體代碼后面再做分析,我們先看Client與Server如何通過Binder完成通信。
首先看Server進程中的代碼,在Service中:
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
private IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
@Override
public void myMethod() throws RemoteException {
Log.d("recluse", "log from remote process");
}
};
}
這里使用匿名內部類擴展了IMyAidlInterface.Stub,并實現了我們所定義的功能方法,這里mStub就是Server進程中的本地Binder對象。在onBinder 中返回,當有客戶端調用bindService()方法時,系統會回到onBind方法。
再看Client中的代碼,在Activity中:
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface mAidlInterface;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, 0);
try {
mAidlInterface.myMethod();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
這里的ServiceConnection也是使用的內部類實現,為了代碼簡便。這樣在連接成功以后,就可以獲取IMyAidlInterface.Stub對象,它實現了IMyInterface接口,在Client進程中就可以使用我們所定義的功能方法,而該方法則是在Server進程中的Service中定義的Stub中實現的,從而完成了進程間調用。那么下面就根據這三段代碼分析內部如何完成進程間通信的。
首先從bindService()方法開始,Client進程就會想SM請求查詢對應的Service,SM找到Server以后,系統回調Service的onBinde()方法,返回Server進程中的Binder對象,也就是我們所定義的IMyInterface.Stub對象,而由于Activity所在的Client進程和Service所在的Server進程并不是同一個進程,在Activity里面的ServiceConnection的onServiceConnection()方法被系統回調時則則傳遞進去Binder本地對象的代理,即BinderProxy對象。此時我們再看Stub提供的靜態方法asInterface(),該方法的功能就是根據Binder對象,返回我們想要的實現我們定義接口,也就是IMyAidlInterface的對象,在該方法中,
/**
* Cast an IBinder object into an com.example.androidlearning.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static IMyAidlInterface asInterface(IBinder obj){
if ((obj==null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof IMyAidlInterface))) {
return ((IMyAidlInterface)iin);
}
return new Stub.Proxy(obj);
}
DESCRIPTOR是一個字符串,是Binder的唯一標識。在這個方法中首先查找本地Binder對象,如果沒有,說明傳遞進來的IBinder是一個BinderProxy對象,此時返回一個Stub.Proxy對象,下面我們來看Stub.Proxy的代碼:
private static class Proxy implements IMyAidlInterface{
private IBinder mRemote;
Proxy(IBinder remote){
mRemote = remote;
}
@Override public IBinder asBinder(){
return mRemote;
}
public String getInterfaceDescriptor(){
return DESCRIPTOR;
}
@Override public void myMethod() throws RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_myMethod, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
Stub.Proxy同樣實現了我們定義的功能接口,而且包含一個BinderProxy對象,當我們在Client進程中調用我們所定義的功能方法時,其實就是調用Stub.Proxy中實現的方法,在實現該功能方法時,它首先將參數序列化,然后調用BinderProxy的transact()方法,調用該方法以后,Binder驅動會喚醒Server進程中的本地Binder對象, 并調用它的onTransact()方法,這個過程有Binder驅動實現,暫時還沒有學習到。最后Binder負責將返回數據序列化返回該方法,寫入到_reply中。下面看Stub的onTransact()方法:
@Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException{
switch (code){
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_myMethod:
{
data.enforceInterface(DESCRIPTOR);
this.myMethod();
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
在Stub類的代碼的最后我們看到定義了TRANSACTION_myMethod,是一個整型,也就是說在Binder中對每一個方法都進行了編號,在transact()方法中傳入編號,然后在onTransact()方法中,根據請求的變化調用相應的方法。這里我們看到data接收參數,然后調用本地Binder中定義的功能方法,這里是抽象方法,留有子類實現,最后將結果寫入到_reply中,由Binder驅動負責將返回值傳遞到BinderProxy的transact()方法中的_reply。到此我們就完成了對于進程間調用的整個過程,分析完了生成的代碼,最后只剩下asBinder方法,這是IInterface中定義的方法,它負責返回對應的IBinder。這里有IBinder和IInterface兩個接口,其中IBinder有兩個實現類Binder和BinderProxy, 所以一個需要繼承或者組合,一個需要實現,當然對于IInterface的實現也可以通過定義其他類實現并組合到Stub或者Stub.Proxy類中,總之具有兩個接口的功能即可。AIDL的生成代碼中,對于IBinder的處理,Stub使用繼承,可以理解,因為需要回調,Stub.Proxy則是使用了組合,其實繼承也可以,但是有什么壞處暫時還不明白。對IInterface則是統一處理,這樣也最簡單。
連接過程可以總結為Client調用bindService, 系統回調Server進程中Service的onBind(), 系統根據onBind()中返回的Binder,返回給Client進程一個對應的BinderProxy, 這個是在onServiceConnected()回調方法中傳遞到Client進程。這樣Client進程持有BinderProxy對象,也就表示與Server建立連接。Client用拿到的BinderProxy或者Binder本地對象(這種是綁定本地服務,不需要進程間通信,如asInterface中,直接返回了自身), 調用靜態方法asInterface()將IBinder對象轉換成為我們所定義的接口對象,該對象可能繼承自Binder或BinderProxy, 也可能組合的方式持有Binder或BinderProxy。這個IBinder對象就像是傳到Client進程中了,同時還可以調用功能方法。
調用過程可以總結為, 在Client進程中使用asInterface()返回的對象調用功能方法,如果是進程間通信,則是序列化參數并調用BinderProxy的transact()方法,有Binder驅動完成中間過程,然后回調本地Binder的onTransact()方法,在該方法中完成遠程方法的調用,并將結果寫會,有Binder驅動再完成中間過程,將寫過傳回到BinderProxy的transact()方法中,從而完成一次進程間通信。
至此,Binder進程間通信的應用層的機制已經分析結束,關于Binder涉及到三個概念,第一個就是Binder驅動,運行在內核中,是進程間通信的關鍵,第二個是Binder類,通過代理模式,在BinderProxy和Binder中建立連接,造成一種可以在進程間傳遞對象的假象,從而使得Binder對象仿佛變成可以傳遞的對象。第三個就是Binder進程間通信機制,其實本文所介紹的整個可以稱之為Binder通信機制。Binder之所以復雜,可能就是因為它的多重身份吧。