Android中Binder的學習

在Android中Binder用於完成IPC(進程間通信),Binder工作在Linux層面,屬於一個驅動,只是這個驅動不需要硬件,或者說其操作的硬件是一小段內存。客戶端調用Binder是通過 系統調用完成的。

Binder簡單介紹

Binder是一種架構,這種架構提供了服務端的接口,Binder的驅動,客戶端接口三個模塊。

首先來看服務端,一個Binder服務端就是一個Binder類的對象,該對象一創建,內部就啓動一個隱藏線程,該線程接下來會接收Binder驅動發送來的消息, 收到消息後,會執行到Binder對象中的onTransact()函數,並按照該函數的參數執行不同的服務代碼。要實現一個Binder服務,必須重載onTransact方法。
重載onTransact函數的主要內容是吧該函數的參數轉換爲服務函數的參數,而onTransact函數的參數來源是客戶端調用transact函數時輸入的,因此,如果transact有固定的格式的輸入,那麼onTransact就會有固定格式的輸出。

下面看看Binder驅動,任意一個服務端Binder對象被創建時,同時會在Binder驅動中創建一個mRemote對象,該對象的類型也是Binder類。客戶端訪問遠程服務時,都是通過mRemote對象。

最後來看看客戶端,客戶端想要訪問遠程服務,必須獲取遠程服務在Binder對象中對應的mRemote引用,獲得該對象,就可以調用其transact方法。

以上就是客戶端通過驅動調用了服務端的代碼。
對應用程序的開發人員來講,客戶端似乎是直接調用遠程服務對應的Binder,而事實上則是通過Binder驅動進行了中轉。即存在兩個Binder對象,一個是服務端的Binder對象,另一個則是Binder驅動中的對象,所不同的是Binder驅動中的對象不會再額外產生一個線程。


獲取Binder對象

事實上, 可以完全不使用Service類,而僅僅基於Binder類編寫服務程序,但是隻是一部分。具體來講,可以僅使用Binder類擴展系統服務,而對於客戶端服務則必須基於Service類來編寫。所謂的系統服務是指可以使用getSystemService方法獲取的服務,所謂的客戶端服務是指應用程序提供的自定義服務。

對於客戶端來講,可以使用以下兩個函數來和服務建立連接,其原型在android.app.ContextImpl類中。

public ComponentName startServce(Intent intent);

該函數用於啓動intent指定的服務,而啓動後,客戶端暫時還沒有服務端的Binder引用,因此,暫時還不能調用任何服務功能。

public boolean bindService(Intent service, ServiceConnection conn, int flags);

該函數用於綁定一個服務,這就是第一個重要問題的關鍵所在。其中第二個參數是一個interface類,該interface的定義如以下代碼所示:

public interface ServiceConnection{
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponetName name);
}

請注意該interface中的onServiceConnected方法的第二個變量service,當客戶端請求AmS啓動某個Service後,該Service如果正常啓動,那麼AmS就會遠程調用ActivityThread類中的ApplicationThread對象,調用的參數中會包含Service的Binder引用,然後在ApplicationThread中會回調binderService中的conn接口。因此,在客戶端中,可以在onServiceConnected方法中將其參數service保存爲一個全局變量,從而在客戶端的任何地方都可以調用該遠程服務。這就是客戶端如何獲取遠程服務的Binder引用。


AIDL工具簡單介紹

此處不進行aidl的使用介紹,及編寫規範。

通過AIDL工具可以把一個aidl文件轉換爲一個java類文件,該java類文件,同時重載了transact和onTransact方法。這些代碼主要完成以下三個任務:

  • 定義一個Java interface,內部包含aidl文件所聲明的服務函數,並且該類基於android.os.IInterfce接口,即需要提供一個asBinder函數。
  • 定義一個Proxy類,該類將作爲客戶端程序訪問服務端的代理。
  • 定義一個Stub類,這是一個abstract類,基於Binder類。

在Stub類中,除了以上所述的任務外,Stub還提供了一個asInterface函數。提供這個函數的作用是這樣的,首先需要明確的是,aidl所產生的代碼完全可以由程序員手工編寫。提供這個函數的原因是服務端提供的服務除了其他進程可以使用外,在服務進程內部的其他類也可以使用該服務,對於後者,顯然是不需要經過IPC調用。

而可以直接在進程內部調用,而Binder內部有個queryLocalInterface(String description)函數。

public static com.cqumonk.calculate.aidl.ICalculateAIDL asInterface(android.os.IBinder obj) { if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//根據包名獲取本地實現的一個接口的實例,如果是本地service則可以獲取到
if (((iin != null) && (iin instanceof com.cqumonk.calculate.aidl.ICalculateAIDL))) { return ((com.cqumonk.calculate.aidl.ICalculateAIDL) iin); //如果得到的實例是ICalculateAIDL的對象,則返回
}
return new com.cqumonk.calculate.aidl.ICalculateAIDL.Stub.Proxy(obj);//如果無法得到本地實現的對象則會返回一個代理對象}

當創建一個Binder對象時,服務端進程內部創建一個Binder對象,Binder驅動中也會創建一個Binder對象。如果從遠程獲取服務端的Binder,則只會返回Binder驅動中的Binder對象,而如果從服務端進程內部獲取Binder對象,則會獲取服務端本身的Binder對象。

系統服務中的Binder對象

在編程時,經常使用getSystemService方法獲取一個系統服務。getSystemService函數的實現是在ContextImpl類中,該函數所返回的Service比較多,這些Service一般都由ServiceManager管理。

ServiceManager是一個獨立進程,本身也是一個Service,Framework提供了一個系統函數,可以獲取該Service對應的Binder引用,那就是BinderInternal.getContextObject()。該靜態函數返回ServiceManager後,就可以通過ServiceManager提供的方法獲取其他系統Service的Binder引用。

以上內容記錄Binder的學習理解。參考《Android內核剖析》。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章