Android N 程序間通訊--Binder
阿新 • • 發佈:2019-02-02
Binder簡介
Binder是Android特有的一種程序間通訊(IPC)方式。Android系統的服務都是通過Binder構建的。Binder是整個系統執行的中樞,因此,Android在提高Binder的效率方法下足了功夫。Android在程序間傳遞資料使用的是共享記憶體的方式,這樣資料只需要複製一次就能從一個程序到達另一個程序,這樣資料傳輸的效率大大提高了。
在安全性方面Android也做了考慮,Binder呼叫時會傳遞呼叫程序的euid到服務端,因此,服務端可以通過檢查呼叫程序的許可權來決定是否允許其使用所呼叫的服務。
Binder物件定義
Binder涉及的物件比較多,這裡對Binder物件做下定義。
- Binder實體物件:Binder實體物件就是Binder服務的提供者。一個提供Binder服務的類必須繼承BBinder類,因此,有時為了強調物件的型別,也用“BBinder物件”代替“Binder的實體物件”。例如,PackageManagerService.java。
- Binder引用物件:Binder引用物件是Binder實體物件在客戶程序的代表,每個引用物件的型別都是BpBinder類,同樣可以用“BpBinder物件”來代替“Binder引用物件”。
- Binder代理物件:代理物件也成為介面物件,它主要是為客戶端的上層應用提供介面服務,從IInterface類派生。它實現了Binder服務的函式介面,當然只是一個轉調的空殼。通過代理物件,應用能像使用本地物件引用使用遠端的實體物件提供的服務。例如,PackageManager.java。
- IBinder物件:BBinder和BpBinder類都是從IBinder類中繼承而來的。在很多場合,不需要刻意地去區分實體物件和引用物件,這時,可以使用“IBinder物件”統一稱呼他們。
Binder代理物件主要和應用程式打交道,將Binder代理物件和引用物件分開的好處是代理物件可以有很多例項,但是,它們包含的是同一個引用物件,這樣方便使用者層的使用。應用完全可以拋開介面物件直接使用Binder引用物件,但是這樣開發的程式相容性不好。也正是在客戶端將引用物件和代理物件分離,Android才能用一套架構來同時為java和native層提供Binder服務。隔離後,Binder底層不需要關心上層的實現細節,只需要和Binder實體物件和引用物件進行互動。
Binder的架構
Binder通訊的參與者由4部分構成。
- Binder驅動:Binder的核心,實現各種Binder的底層操作。
- ServiceManager:提供Binder的名稱到引用物件的轉換服務。
- 服務端:Binder服務的提供者。
- 客戶端:Binder服務的使用者。
Binder驅動位於Binder架構的核心,通過檔案系統的標準介面,如open()、ioctl()、mmap()等,向用戶層提供服務。應用層和Binder驅動之間的資料交換是通過ioctl()介面完成的,這樣一次系統呼叫就能完成使用者系統和驅動之間的雙向資料交換,提高了傳輸效率。Binder驅動的主要功能是提供Binder通訊的通道,維護Binder物件的引用計數,轉換傳輸中的Binder實體物件和引用物件以及管理資料緩衝區。
ServiceManager是一個守護程序,它的作用是提供Binder服務的查詢功能,返回被查詢服務的引用。對於一個Binder服務,通常會有一個惟一的字串標識,只要它向ServiceManager註冊了這個標識,應用就可以通過標識來獲得服務的引用物件。ServiceManager是一個單獨的程序,實際上也是通過Binder框架來提供服務。
既然驅動是Binder的核心,為什麼不直接把查詢引用物件的功能放在驅動中完成呢,還要在ServiceManager的程序中繞一圈?其實這和Android的安全管理有關係。Android中並不容許任意程序都能註冊Binder服務,雖然任意一個程序都能建立Binder服務,但是隻有root程序或system程序才可以不受限制地向ServiceManager註冊服務。ServiceManager中有一張表,裡面規定了能夠註冊服務的程序的使用者ID,以及程序能夠註冊的服務的名稱,ServiceManager通過這張表來控制普通程序的註冊請求(Android5.0通過SELinux而不是查表的方式來檢查許可權)。這也是ServiceManager存在的原因。
Binder服務可以分成兩種:實名服務和匿名服務。他們從開發到使用沒有任何區別,惟一的區別是實名服務能夠通過ServiceManager查詢到。Android中的實名Binder服務都是系統提供的,如ActivityManagerService、PackageManagerService、WindowManagerService等。普通應用開發的Binder服務,只能是匿名服務。
如果匿名服務不能通過ServiceManager查詢到,使用者是通過什麼方式才得到它的引用物件呢?仍然是通過Binder。匿名服務經常使用的場景是服務程序回撥客戶程序中的函式。整個過程是:客戶端和服務端通過Binder連線上後,客戶端把本程序中建立的匿名服務的實體物件作為函式引數傳遞到服務端,驅動會在中間把實體物件轉換成引用物件,這樣服務程序就得到了客戶程序建立的Binder服務的引用物件,然後就能回撥客戶程序中Binder服務的函數了。
在java層Android還提供了通過元件Service的方式來包裝和使用匿名服務。
對Binder框架的理解,可參考http://www.2cto.com/kf/201606/515548.html,理解起來比較簡單。
元件Service和匿名Binder服務
匿名Binder服務因為沒有ServiceManager來提供名稱解析服務,因此一般只能用於服務程序回撥客戶程序。但是,Android還通過Framework提供了一種啟動java匿名Binder服務的方法。這種方法的過程如下:首先某個應用通過呼叫bindService()方法發出一個Intent,Framework根據Intent找到對應的元件Service並啟動它,包在元件Service中的Binder服務也將同時創建出來。隨後Framework會把服務的IBinder物件通過ConnectivityManager的回撥方法onServiceConnected()傳回到應用,這樣應用就得到了匿名Binder服務的引用物件,也就能使用元件Service中的匿名服務了。
在這裡Android的Framework用Intent代替了Binder服務的名稱來查詢對應的服務,同時也承擔了ServiceManager的工作,解析Intent並傳回服務的引用物件。
Binder的層次
從程式碼實現上劃分,Binder設計的類可以分成4個層次。
最上層是位於Framework中的各種Binder服務類和他們的介面類。例如,ActivityManagerService、WindowManagerService、PackageManagerService等,他們為應用程式提供了多種多樣的服務。
最底層的是Binder驅動。
中間則分兩層,上層是用於服務類和介面類開發的基礎,如IBinder、BBinder、BpBinder等。下層是和驅動互動的IPCThreadState和ProcessState類。
如何使用Binder
下面說一下如何使用Binder,分為兩部分,一是如何使用已有的Binder服務,二是如何開發Binder服務。
就開發語言而言,Binder服務可以有java實現,也可以用C++實現。但從原理上講,Binder服務並沒有這種限制,也可以混合呼叫。因此下面就不區分語言了。
使用Binder服務
使用Binder服務前要先得到它的引用物件。例如得到CameraService的引用物件,可以這樣使用:
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm.getService("media.camera");
defaultServiceManager()方法用來得到ServiceManager服務的引用物件。ServiceManager的getService()方法用來查詢註冊的Binder服務,如果getService()找到名稱對應的服務,它會返回服務的IBinder物件,否則返回Null。
getService()方法返回型別是IBinder,實際上是引用物件。而應用需要使用的是Binder服務的代理物件,因此,在使用之前要將引用物件轉換為代理物件,方法如下:
ICameraService service = ICameraService.Stub.asInterface(binder);
Binder中提供了asInterface()方法來完成這種轉換,該方法會檢查引數中的IBinder物件的服務型別是否相符,如果不符返回NULL。得到了服務的代理物件後,接下來就可以想使用普通物件一樣使用Binder物件了。
正常情況下,ServiceManager程序會在所有應用啟動前啟動,而且也不會停止服務,因此,使用defaultServiceManager方法時可以不檢查他的返回值是否為NULL,但是,對方法getService的返回值則需要檢查,因為查詢的服務有可能沒有啟動。
用java開發Binder服務
這裡以元件Service為例,Service裡面包含一個匿名的Binder服務。
(1)、第一步,編寫AIDL檔案,如下:
package com.sgf.intface;
interface IExampleService {
int get();
void set(int value);
}
AIDL是Android Interface Describation Language的縮寫,它是Android提供的一種用來簡化Binder程式設計的指令碼語言。開發人員只需在AIDL中定義出方法介面,AIDL直譯器能自動生成伺服器和客戶端需要的java程式碼。
(2)、第二步,編寫Service程式碼:
package com.sgf.service;
import com.sgf.intface.IExampleService;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class ExampleService extends Service {
int mValue = 0;
private final IExampleService.Stub mInstance = new IExampleService.Stub() {
@Override
public void set(int value) throws RemoteException {
// TODO Auto-generated method stub
mValue = value;
}
@Override
public int get() throws RemoteException {
// TODO Auto-generated method stub
return mValue;
}
};
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return mInstance;
}
}
ExampleService繼承Service,需要過載onBind()方法,並在方法中返回Binder服務的實體物件mInstance,IExampleService.Stub類是真正的Binder服務類,它是通過前面的aidl檔案自動生成的,因此,這裡還需要繼承IExampleService.Stub並重載它的方法,還要實現方法的功能。
(3)、第三步,在AndroidManifest中加入服務的宣告:
<service android:name=".service.ExampleService" >
<intent-filter>
<action android:name="com.sgf.intface.IExampleService" />
</intent-filter>
</service>
這裡加入宣告的作用是為了讓Framework通過Intent找到該service。
針對上面,舉個系統服務(PackageManagerService)的例子。
- AIDL檔案:IPackageManager.aidl。檔案中定義了很多介面,具體可檢視原始碼。
- Binder服務類例項:PackageManagerService。PackageManagerService繼承IPackageManager.Stub,在PackageManagerService中過載IPackageManager.aidl的方法,並實現。
- Binder服務的代理物件:PackageManager。PackageManager類為提供給第三方APP的介面類。
用C++開發Binder服務
此處可參考:http://book.51cto.com/art/201504/474431.htm。
Binder應用層的核心類
上面我們使用了一些libbinder庫中提供的類來開發Binder服務。libbinder庫中的IInterface類、BpInterface類、BnInterface類、BBinder類、BpBinder類、IBinder類共同構成了Binder應用層的核心類。
IInterface中的兩個巨集
在IInterface.h中有兩個巨集,DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE。
巨集DECLARE_META_INTERFACE定義如下:
#define DECLARE_META_INTERFACE(INTERFACE) \
static const android::String16 descriptor; \
static android::sp<I##INTERFACE> asInterface( \
const android::sp<android::IBinder>& obj); \
virtual const android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
這裡定義了靜態成員變數descriptor,靜態函式asInterface(),以及類的構造和解構函式。可能是因為這些變數和函式是每個Binder服務的實現這都需要提供的,為了方便開發,Android把他們定義成了巨集。開發服務時直接在類定義裡使用這個巨集,方便也不容易出錯。
而巨集IMPLEMENT_META_INTERFACE是這些函式的實現,定義如下:
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
const android::String16 I##INTERFACE::descriptor(NAME); \
const android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
const android::sp<android::IBinder>& obj) \
{ \
android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = new Bp##INTERFACE(obj); \
} \
} \
return intr; \
} \
I##INTERFACE::I##INTERFACE() { } \
I##INTERFACE::~I##INTERFACE() { } \
變數descriptor是一個字串,描述了Binder服務,在檢查從客戶端傳遞的引數是否合法時需要用到它。函式asInterface()主要是用來把Binder的引用物件轉換成代理物件。