Binder 是 Android 系統中很是重要的組成部分。Android 系統中的許多功能創建在 Binder 機制之上。在這篇文章中,咱們會對 Android 中的 Binder 在系統架構中的做用進行分析;而後,咱們會從底層的實現角度簡要說明爲何 Android 要開發出一套獨立的跨進程通訊機制;最後,咱們會給出一個 AIDL 的使用示例來講明如何使用 Binder 來進行通訊。java
「什麼是 Binder? 爲何說它對 Android 系統相當重要?」 在回答這個問題以前,咱們先來講下其餘的東西。android
不知道你有沒有思考過這麼一個問題:爲何當咱們在 Android 中啓動一個頁面的時候須要調用 startActivity()
方法,而後還要傳入一個 Intent? 若是咱們不使用這種傳遞值的方式,直接寫成靜態的變量有沒有問題?這也是以前有人問過個人一個問題。c++
對上面的兩個問題,咱們先回答第二個。使用靜態的變量傳遞值在大部分狀況下是能夠的,固然要注意在使用完了值以後要及時釋放資源,否則會佔用太多內存,甚至 OOM. 可是,在特殊的狀況下它是沒法適用的,即跨進程的狀況下。這是由於,靜態的變量的做用範圍只是其所在的進程,在其餘進程訪問的時候屬於跨進程訪問,固然訪問不到了。對於第一個問題,Android 中的一個 Activity 的啓動過程遠比咱們想象的複雜,其中就涉及跨進程的通訊過程。當咱們調用 startActivity()
方法以後,咱們的全部的 「意圖」 會通過層層過濾,直到一個稱之爲 AMS 的地方被處理。處理完以後,再跨進程調用你啓動頁面時的進程進行後續處理,即回調 onCreate()
等生命週期方法。git
一個 Activity 的啓動過程涉及 Android 中兩種重要的通訊機制,Binder 和 Handler,咱們會在之後的文章中對此進行分析。github
下面咱們經過一個簡單的圖來講明一下 Activity 的啓動過程:設計模式
當咱們調用 startActivity()
方法的時候,首先會從 ServiceManager 中獲取到 ActivityManagerService (就是 AMS),而後將 ApplicationThread 做爲參數傳遞給 AMS,而後執行 AMS 的方法來啓動 Activity. (在咱們的應用進程中執行另外一個進程的方法。)api
AMS 是全局的,在系統啓動的時候被啓動。當咱們使用它的時候從 ServiceManager 中獲取這個全局的變量便可。當咱們調用它的方法的時候,方法具體的執行邏輯將在系統的進程中執行。咱們傳入的 ApplicationThread 就像一個信使同樣。當 AMS 處理完畢,決定回調 Activity 的生命週期方法的時候,就直接調用 ApplicationThread 的方法(這是在另外一個進程中調用咱們的應用進程)。這樣就實現了咱們的 Activity 的生命週期的回調。緩存
看了上面的過程,也許有的同窗會以爲。Binder 對 Android 系統相當重要,可是咱們並無用到 Binder 啊。實際上,咱們只是沒有直接使用 Binder. 如下圖爲例,咱們說下咱們實際開發過程當中是如何使用 Binder 的。安全
在大多數狀況下,咱們都在與各個 Manager 進行交互,而實際上這些 Manager 內部是使用 Binder 來進行跨進程通訊的。如上所示,當咱們調用 Manager 的時候,Manager 會經過代理類來從 Binder 驅動中獲得另外一個進程的 Stub 對象,而後咱們使用該 Stub 對象,遠程調用另外一個進程的方法。只是這個過程被封裝了,咱們沒有感知到而已,而這個跨進程通訊 (IPC) 的機制就是 Binder 機制。服務器
至於什麼是 Stub 呢?Stub 是 AIDL 規範中的一部分。AIDL 爲咱們使用 Binder 提供了一套模板。在 Android 系統中大量使用了這種定義來完成跨進程通訊。稍後咱們介紹 AIDL 的時候,你將看到它是如何做用的。
Android 是基於 Linux 的,Linux 自己已經具備了許多的 IPC 機制,好比:管道(Pipe)、信號(Signal)和跟蹤(Trace)、插口(Socket)、消息隊列(Message)、共享內存(Share Memory)和信號量(Semaphore)。那麼,爲何 Android 要特立獨行地搞出一套 IPC 機制呢?這固然是有緣由的:
效率上 :Socket 做爲一款通用接口,其傳輸效率低,開銷大,主要用在跨網絡的進程間通訊和本機上進程間的低速通訊。消息隊列和管道採用存儲-轉發方式,即數據先從發送方緩存區拷貝到內核開闢的緩存區中,而後再從內核緩存區拷貝到接收方緩存區,至少有兩次拷貝過程。共享內存雖然無需拷貝,但控制複雜,難以使用。Binder 只須要一次數據拷貝,性能上僅次於共享內存。
穩定性:Binder 基於 C|S 架構,客戶端(Client)有什麼需求就丟給服務端(Server)去完成,架構清晰、職責明確又相互獨立,天然穩定性更好。 共享內存雖然無需拷貝,可是控制負責,難以使用。從穩定性的角度講,Binder 機制是優於內存共享的。
安全性:Binder 經過在內核層爲客戶端添加身份標誌 UID|PID
,來做爲身份校驗的標誌,保障了通訊的安全性。 傳統 IPC 訪問接入點是開放的,沒法創建私有通道。好比,命名管道的名稱,SystemV 的鍵值,Socket 的 ip 地址或文件名都是開放的,只要知道這些接入點的程序均可以和對端創建鏈接,無論怎樣都沒法阻止惡意程序經過猜想接收方地址得到鏈接。
除了上面的緣由以外,Binder 還擁有許多其餘的特性,好比:1).採用引用計數,當某個 Binder 再也不被任何客戶端引用的時候,會通知它的持有者能夠將其釋放,這適用於 Android 這種經常由於資源不足而回收資源的應用場景。2).它內部維護了一個線程池;3).能夠像觸發本地方法同樣觸發遠程的方法。4).支持同步和異步 (oneway) 的觸發模型;5).可使用 AIDL 模板進行描述和開發。
在 Binder 模型中共有 4 個主要角色,它們分別是:Client、Server、Binder 驅動和 ServiceManager. Binder 的總體結構是基於 C|S 結構的,以咱們啓動 Activity 的過程爲例,每一個應用都會與 AMS 進行交互,當它們拿到了 AMS 的 Binder 以後就像是拿到了網絡接口同樣能夠進行訪問。若是咱們將 Binder 和網絡的訪問過程進行類比,那麼 Server 就是服務器,Client 是客戶終端,ServiceManager 是域名服務器(DNS),驅動是路由器。其中 Server、Client 和 ServiceManager 運行於用戶空間,驅動運行於內核空間。
當咱們的系統啓動的時候,會在啓動 SystemServer 進程的時候啓動各個服務,也包括上面的 AMS. 它們會被放進一個哈希表中,而且哈希表的鍵是字符串。這樣咱們就能夠經過服務的字符串名稱來找到對應的服務。這些服務就是一個個的 Binder 實體,對於 AMS 而言,也就是 IActivityManager.Stub
實例。這些服務被啓動的以後就像網絡中的服務器同樣一直等待用戶的訪問。
對於這裏的 ServiceManager,它也是一種服務,可是它比較特殊,它會在全部其餘的服務以前被註冊,而且只被註冊一次。它的做用是用來根據字符串的名稱從哈希表中查找服務,以及在系統啓動的時候向哈希表中註冊服務。
因此,咱們可使用上面的這張圖來描述整個 Binder 模型:首先,在系統會將應用程序所需的各類服務經過 Binder 驅動註冊到系統中(ServiceManager 先被註冊,以後其餘服務再經過 ServiceManager 進行註冊),而後當某個客戶端須要使用某個服務的時候,也須要與 Binder 驅動進行交互,Binder 會經過服務的名稱到 ServiceManager 中查找指定的服務,並將其返回給客戶端程序進行使用。
上面咱們梳理了 Binder 的模型,以及爲何系統設計一套通訊機制的緣由。那麼你是否也好奇神乎其神的 Binder 到底是怎麼實現的呢?這裏咱們來梳理下 Binder 內部實現的原理。
首先,Binder 的實現過程是很是複雜的,在《Android 系統源碼情景分析》一書中有 200 頁的篇幅都在講 Binder. 在這裏咱們不算詳細地講解它的具體的實現原理,咱們只對其中部份內容作簡單的分析,而且不但願涉及大量的代碼。
而後,咱們須要介紹下 Binder 相關的核心類在源碼中的位置,
-framework
|--base
|--core
|--java--android--os
|--IInterface.java
|--IBinder.java
|--Parcel.java
|-- IServiceManager.java
|--ServiceManager.java
|--ServiceManagerNative.java
|--Binder.java
|--jni
|--android_os_Parcel.cpp
|--AndroidRuntime.cpp
|--android_util_Binder.cpp
|--native
|--libs--binder
|--IServiceManager.cpp
|--BpBinder.cpp
|--Binder.cpp // Binder 的具體實現
|--IPCThreadState.cpp
|--ProcessState.cpp
|--include--binder // 主要是一些頭文件
|--IServiceManager.h
|--IInterface.h
|--cmds--servicemanager
|--service_manager.c // 用來註冊服務的 ServiceManager
|--binder.c
-kernel-drivers-staging-android
|--binder.c
|--uapi-binder.h
複製代碼
當咱們查看 binder.c 的源碼的時候,或者查看與 Binder 相關的操做的時候,常常看到幾個操做 ioctl, mmap 和 open. 那麼這幾個操做符是什麼含義呢?
首先,open
函數用來打開文件的操做符,在使用的時候須要引入頭文件,#include <sys/types.h>
、#include <sys/stat.h>
和 #include <fcntl.h>
,其函數定義以下,
int open(const char * pathname, int flags);
int open(const char * pathname, int flags, mode_t mode);
複製代碼
這裏的 pathname
表示文件路徑;flag
表示打開方式;mode
表示打開的模式和權限等;若全部欲覈查的權限都經過了檢查則返回 0, 表示成功, 只要有一個權限被禁止則返回-1.
而後是 ioctl
指令,使用的時候須要引入 #include <sys/ioctl.h>
頭文件,ioctl 是設備驅動程序中對設備的 I/O 通道進行管理的函數,用於向設備發控制和配置命令。其函數定義以下:
int ioctl(int fd, ind cmd, …);
複製代碼
其中 fd 是用戶程序打開設備時使用 open 函數返回的文件標示符,cmd 是用戶程序對設備的控制命令,至於後面的省略號,那是一些補充參數,通常最多一個,這個參數的有無和 cmd 的意義相關。
最後是 mmap
函數,它用來實現內存映射。使用的時候須要引入頭文件 #include <sys/mman.h>
. 與之對應的還有 munmap
函數。它們的函數定義以下,
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);
複製代碼
這裏的參數的含義是:
open()
函數返回,其值也能夠設置爲-1,此時須要指定 flags 參數中的 MAP_ANON,代表進行的是匿名映射;成功執行時,mmap()
返回被映射區的指針,munmap()
返回0。失敗時,mmap()
返回 MAP_FAILED[其值爲(void *)-1],munmap()
返回 -1.
Binder 中的 ServiceManager 並不是 Java 層的 ServiceManager,而是 Native 層的。啓動 ServiceManager 由 init 進程經過解析 init.rc 文件而建立。啓動的時候會找到上述源碼目錄中的 service_manager.c 文件中,並調用它的 main() 方法,
// platform/framework/native/cmds/servicemanager.c
int main(int argc, char** argv) {
struct binder_state *bs;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
// 1. 打開 binder 驅動
bs = binder_open(driver, 128*1024);
// ...
// 2. 將當前的 ServiceManger 設置成上下文
if (binder_become_context_manager(bs)) {
return -1;
}
// ...
// 3. 啓動 binder 循環,進入不斷監聽狀態
binder_loop(bs, svcmgr_handler);
return 0;
}
複製代碼
ServcieManager 啓動的過程就是上面三個步驟,無需過多說明。下面咱們給出這三個方法具體實現的。在下面的代碼中你將看到咱們以前介紹的三個函數的實際應用。相應有了前面的鋪墊以後你理解起來不成問題 :)
// platform/framework/native/cmds/servicemanager.c
struct binder_state *binder_open(const char* driver, size_t mapsize) {
struct binder_state *bs;
struct binder_version vers;
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
// 打開設備驅動
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
if (bs->fd < 0) {
goto fail_open;
}
// 向驅動發送指令,獲取binder版本信息
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
goto fail_open;
}
bs->mapsize = mapsize;
// 經過系統調用,mmap 內存映射,mmap 必須是 page 的整數倍
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
goto fail_map;
}
return bs;
fail_map:
close(bs->fd);
fail_open:
free(bs);
return NULL;
}
複製代碼
在上面的代碼中,先使用 open()
函數打開設備驅動(就是一個打開文件的操做),而後使用 ioctl()
函數向上面的設備驅動發送指令以獲取設備信息。最後,經過 mmap()
函數實現內存映射,並將上述的文件描述符傳入。這裏的 binder_state 是一個結構體,定義以下。其實就是用來描述 binder 的狀態。從上面咱們也能看到它的三個變量的賦值過程。
// platform/framework/native/cmds/servicemanager.c
struct binder_state {
int fd;
void *mapped;
size_t mapsize;
};
複製代碼
固然,在上面的代碼中,咱們又見到了久違的 goto 指令。它們主要用來處理髮生一些異常的狀況。
打開了驅動以後,註冊爲上下文的方法更加簡單,
// platform/framework/native/cmds/servicemanager.c
int binder_become_context_manager(struct binder_state *bs) {
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
複製代碼
就是一個 ioctl
函數,使用指令 BINDER_SET_CONTEXT_MGR
將當前的 ServiceManager 註冊爲上下文。
最後就是啓動 Binder 循環了。它的邏輯也沒有想象中得複雜,就是啓動了 for 循環,
// platform/framework/native/cmds/servicemanager.c
void binder_loop(struct binder_state *bs, binder_handler func) {
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
// 將 BC_ENTER_LOOPER 命令發送給 binder 驅動,內部調用 ioctl 函數
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// 使用 iotcl 函數讀取
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
break;
}
// 解析
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
break;
}
if (res < 0) {
break;
}
}
}
複製代碼
從上面看出,函數將會在 binder_write() 中將命令發送給 Binder 驅動,以啓動循環。其實內部也是調用 ioctl 函數實現的。而後程序會啓動一個循環來不斷讀取、解析。這是服務器很典型的操做了。
固然,咱們上面分析的是 ServiceManager 中向 Binder 寫命令的過程,而驅動如何解析呢?固然是在驅動中實現了,詳細的過程能夠查看 Binder 驅動部分的源碼。
下面咱們以 AMS 做爲例子來說解下 Binder 跨進程通訊的實現過程。首先,當咱們調用 startActivity()
方法的時候,最終將會進入 ActivityManager 以獲取 AMS,
// platform/framework/base/core/java/android/app/ActivityManager.java
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
複製代碼
這裏會使用 ServiceManger 來按名稱查找 AMS,查找到 Binder 對象以後將其轉換成 AMS 就可使用了。以前,咱們也說過用來查找 AMS 的 SeerviceManager 自己也是一種服務。因此,它這裏的方法也是經過 Binder 來實現的。那麼,咱們就從這裏的 getService()
方法入手。
// platform/framework/base/core/java/android/os/ServiceManager.java
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) { /* ... */ }
return null;
}
複製代碼
這裏會先嚐試從緩存當中取 Binder,取不到的話就從遠程進行獲取。這裏使用 rawGetService()
方法來從遠程獲取 Binder,代碼以下,
// platform/framework/base/core/java/android/os/ServiceManager.java
private static IBinder rawGetService(String name) throws RemoteException {
final IBinder binder = getIServiceManager().getService(name);
// ...
return binder;
}
// platform/framework/base/core/java/android/os/ServiceManager.java
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
複製代碼
在 rawGetService()
方法中會使用 ServiceManagerNative
的 getService()
方法從遠程獲取 Binder. 這裏的 ServiceManagerNative 本質上只是一個代理類,它實際的邏輯是由 BinderInternal.getContextObject()
返回的 Binder 實現的。
也許你已經暈了,怎麼那麼多 Binder……我來講明下。當要查找 AMS 的時候其實是一個跨進程的調用過程,也就是實際的查找的邏輯是在另外一個進程實現,所以須要 Binder 來通訊。而查找 AMS 的遠程對象實際上就是咱們上面所說的 ServiceManager (Native 層的而不是 Java 層的,Java 層的 ServiceManager 是一個代理類,是用來從遠程獲取服務的)。
所以,按照上面的描述,BinderInternal.getContextObject()
返回的就應該是遠程的 Binder 對象。因而方法進入 Native 層,
// platform/framework/base/core/jni/android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz) {
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
複製代碼
這裏的 ProcessState::self()
是否熟悉呢?你是否還記得在上一篇文章中,咱們介紹 Android 系統啓動過程的時候介紹過它。咱們曾經使用它來開啓 Binder 的線程池。這裏的 self()
方法實際上是用來獲取一個單例對象的。咱們能夠直接由 getContextObject()
進入 getStrongProxyForHandle()
方法。從下面的方法中咱們能夠看出,這裏調用了 BpBinder
的 create()
方法建立了一個 BpBinder 實例並返回,也就是咱們的 ServiceManager.
// plaftorm/framework/native/libs/binder/ProcessState.cpp
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
IBinder* b = e->binder;
if (b == nullptr || !e->refs->attemptIncWeak(this)) {
// ...
// 調用 BpBinder
b = BpBinder::create(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
複製代碼
當咱們拿到了 ServiceManager 的 Binder 以後就能夠調用它的 getService()
方法來獲取服務了,
// platform/framework/base/core/java/android/os/ServiceManagerNative.java
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
複製代碼
這裏的 mRemote 就是以前返回的 BpBinder,這裏調用它的 transact()
方法,並傳入了一個方法標記 GET_SERVICE_TRANSACTION.
// platform/framework/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
if (mAlive) {
status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
複製代碼
顯然這裏會調用 IPCThreadState 的 self()
方法先獲取一個單例的對象,而後調用它的 transact()
方法繼續方法的執行。
// platform/framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle, uint32_t code,
const Parcel& data, Parcel* reply, uint32_t flags)
{
status_t err;
// ...
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
// ...
if ((flags & TF_ONE_WAY) == 0) { // OneWay 類型的調用,同步的
// ...
if (reply) {
// 等待相應
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
if (reply) alog << indent << *reply << dedent << endl;
else alog << "(none requested)" << endl;
}
} else { // 異步的
err = waitForResponse(nullptr, nullptr);
}
return err;
}
複製代碼
上面會調用 writeTransactionData()
方法用來將數據寫入到 Parcel 中。而後將會進入 waitForResponse()
方法處理與 ServiceManager 交互的結果。而真實的交互發生的地方位於 talkWithDriver()
方法,
// platform/framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
// 經過 ioctl 讀寫操做,與 Binder Driver 進行交互
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
} while (err == -EINTR);
// ...
return err;
}
複製代碼
binder_write_read 結構體用來與 Binder 設備交換數據的結構, 經過 ioctl 與 mDriverFD 通訊,是真正與 Binder 驅動進行數據讀寫交互的過程。先向service manager進程發送查詢服務的請求(BR_TRANSACTION)。而後,service manager 會在以前開啓的循環中監聽到,並使用 svcmgr_handler()
方法進行處理。
// platform/framework/native/cmds/servicemanager.c
int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply) {
// ...
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
case SVC_MGR_ADD_SERVICE: // ...
case SVC_MGR_LIST_SERVICES: // ...
}
return 0;
}
複製代碼
顯然,這裏會從 binder_transaction_data 中取出 code,即 SVC_MGR_GET_SERVICE,而後使用 do_find_service()
方法查找服務。而後再 binder_send_reply() 應答發起者將結果返回便可。
上面咱們梳理了 Binder 通訊的過程,從上面咱們彷佛並無看到能證實 Binder 高效的證據。那麼 Binder 究竟靠什麼實現高效的呢?
實際上,Binder 之因此高效,從咱們上面的代碼還真看不出來。由於,咱們上面的代碼並無涉及 Binder 驅動部分。正如咱們以前描述的那樣,ServiceManager、客戶端和服務器實際是靠 Binder 驅動這個中間媒介進行交互的。而 Binder 高效的地方就發生在 Binder 驅動部分。
就像圖片描述的那樣,當兩個進程之間須要通訊的時候,Binder 驅動會在兩個進程之間創建兩個映射關係:內核緩存區和內核中數據接收緩存區之間的映射關係,以及內核中數據接收緩存區和接收進程用戶空間地址的映射關係。這樣,當把數據從 1 個用戶空間拷貝到內核緩衝區的時候,就至關於拷貝到了另外一個用戶空間中。這樣只須要作一次拷貝,省去了內核中暫存這個步驟,提高了一倍的性能。實現內存映射靠的就是上面的 mmap()
函數。
Binder
本質上只是一種底層通訊方式,和具體服務沒有關係。爲了提供具體服務,Server
必須提供一套接口函數以便 Client
經過遠程訪問使用各類服務。這時一般採用代理設計模式:將接口函數定義在一個抽象類中,Server
和 Client
都會以該抽象類爲基類實現全部接口函數。所不一樣的是 Server
端是真正的功能實現,而 Client
端是對這些函數遠程調用請求的包裝。爲了簡化這種設計模式,Android 中提供了 AIDL 供咱們使用。下文中咱們會介紹 AIDL 相關的內容以及它的一些基本的使用方式。
AIDL (Android Interface Definition Language,Android 接口定義語言)
是一種文件格式,用來簡化 Binder 的使用。當使用 Binder 的時候,只須要建立一個後綴名爲 .aidl
的文件,而後像定義接口同樣定義方法。定義完畢以後,使用工具 aidl.exe
便可生成 Binder 所須要的各類文件。固然,咱們的 AS 已經爲咱們集成了 aidl.exe
,因此,只須要在定義了 AIDL 文件以後,編譯便可生成使用 Binder
時所需的文件。固然,不使用 AIDL,直接編寫 Binder 所需的 java 文件也是能夠的。
AIDL 是一種接口定義語言,它與 Java 中定義接口的方式有所區別。下面咱們經過一個例子來講明 AIDL 的使用方式。
這裏咱們模擬一個筆記管理的類,經過在 Activity 中與一個遠程的 Service 進行交互來實現 IPC 的效果。這裏,咱們先要定義數據實體 Note,它只包含兩個字段,而且實現了 Parcelable。這裏 Note 所在的目錄是 me.shouheng.advanced.aidl
,而後,咱們須要在 src/main
創建一個一樣的包路徑,而後定義所需的 AIDL 文件:
// INoteManager.aidl
package me.shouheng.advanced.aidl;
import me.shouheng.advanced.aidl.Note;
interface INoteManager {
Note getNote(long id);
void addNote(long id, String name);
}
// Note.aidl
package me.shouheng.advanced.aidl;
parcelable Note;
複製代碼
注意,在 INoteManager 文件中,咱們定義了遠程服務所需的各類方法。這裏只定義了兩個方法,一個用來獲取指定 id
的筆記,一個用來向遠程服務中添加一條筆記記錄。
這樣定義完了以後,咱們能夠對項目進行編譯,這樣就能夠 build 目錄下面獲得爲咱們生成好的 INoteManager 類文件。之後,咱們就可使用這個文件中生成類和方法來進行遠程通訊。但在使用該接口以前,咱們仍是先來看一下其中都生成了些什麼東西:
package me.shouheng.advanced.aidl;
public interface INoteManager extends android.os.IInterface {
// 交給遠程來實現具體的業務邏輯
public static abstract class Stub extends android.os.Binder implements me.shouheng.advanced.aidl.INoteManager {
private static final java.lang.String DESCRIPTOR = "me.shouheng.advanced.aidl.INoteManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
// 使用代理包裝遠程對象
public static me.shouheng.advanced.aidl.INoteManager asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof me.shouheng.advanced.aidl.INoteManager))) {
return ((me.shouheng.advanced.aidl.INoteManager)iin);
}
// 返回代理對象
return new me.shouheng.advanced.aidl.INoteManager.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_getNote: {
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
// 使用模板方法來實現業務
me.shouheng.advanced.aidl.Note _result = this.getNote(_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;
}
case TRANSACTION_addNote: {
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
java.lang.String _arg1;
_arg1 = data.readString();
// 使用模板方法來實現業務
this.addNote(_arg0, _arg1);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 代理對象,包裝了遠程對象,內部調用遠程對象獲取遠程的服務信息
private static class Proxy implements me.shouheng.advanced.aidl.INoteManager {
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 me.shouheng.advanced.aidl.Note getNote(long id) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
me.shouheng.advanced.aidl.Note _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(id);
// 實際內部調用遠程對象,在另外一個進程實現業務邏輯
mRemote.transact(Stub.TRANSACTION_getNote, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = me.shouheng.advanced.aidl.Note.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addNote(long id, java.lang.String name) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(id);
_data.writeString(name);
// 實際內部調用遠程對象,在另外一個進程實現業務邏輯
mRemote.transact(Stub.TRANSACTION_addNote, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
// 方法 id,用來標記當前調用的是哪一個方法
static final int TRANSACTION_getNote = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addNote = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public me.shouheng.advanced.aidl.Note getNote(long id) throws android.os.RemoteException;
public void addNote(long id, java.lang.String name) throws android.os.RemoteException;
}
複製代碼
若是隻是看這上面的生成的代碼,也許你仍然沒法瞭解這些生成的類究竟有什麼做用。下面就讓咱們經過使用上面生成的類來講明 AIDL 的具體工做流程。
首先,咱們要定義遠程的服務,並在該服務中實現業務邏輯:
public class NoteService extends Service {
private CopyOnWriteArrayList<Note> notes = new CopyOnWriteArrayList<>();
// 當前服務運行於另外一個進程,這裏實現業務邏輯
private Binder binder = new INoteManager.Stub() {
@Override
public Note getNote(long id) {
return Observable.fromIterable(notes).filter(note -> note.id == id).singleOrError().blockingGet();
}
@Override
public void addNote(long id, String name) {
notes.add(new Note(id, name));
}
};
@Override
public void onCreate() {
super.onCreate();
notes.add(new Note(100, "Note 100"));
notes.add(new Note(101, "Note 101"));
}
// 將 binder 返回,客戶端可使用鏈接來獲取並調用
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
複製代碼
這裏在 onCreate()
方法中建立了兩條記錄,而且建立了 INoteManager.Stub
的實例,並在 onBind()
方法中將其返回。而後,咱們在一個 Activity
中啓動該遠程服務,並嘗試從該服務中獲取指定 id
的筆記記錄。從指望的結果來看,它的功能有些相似於 ContentProvider
,即用來向調用者提供數據。
下面是該 Activity
的實現。這裏咱們在 onCreate()
方法中啓動上述服務。並將實例化的 ServiceConnection
做爲參數啓動該服務。在 ServiceConnection
的方法中,咱們調用 INoteManager.Stub
的 asInterface(IBinder)
方法來說 service
轉換成 INoteManager
,而後從其中獲取指定 id
的筆記記錄便可。
// 建立服務鏈接
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 返回代理對象
INoteManager noteManager = INoteManager.Stub.asInterface(service);
try {
// 使用代理對象
Note note = noteManager.getNote(100);
LogUtils.d(note);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) { }
};
@Override
protected void doCreateView(Bundle savedInstanceState) {
Intent intent = new Intent(this, NoteService.class);
// 綁定服務
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解綁服務
unbindService(connection);
}
}
複製代碼
根據 INoteManager.Stub
的 asInterface()
方法的定義,該方法中會將傳入的 service
包裝成一個 INoteManager.Stub.Proxy
返回,因此,咱們在 onServiceConnected()
方法中實際調用的是該代理類的 getNote()
方法。而該代理類的 getNote()
方法中又調用了傳入的 mRemote.transact()
方法。而這裏的 service
正是咱們在 NoteService
中建立的 binder
。也就是說,當咱們在 onServiceConnected()
中調用 getNote()
方法的時候,實際上調用了 INoteManager.Stub
的 transact()
方法。
因此,從上面咱們看出:
Binder
來實現的。INoteManager.Stub
的 transact()
方法的時候,經過傳入了一個整型的 code
來做爲要觸發的方法的標識,這就是咱們上面提到的方法的編號。因而,咱們能夠經過下面的這張圖來總結在上面使用 AIDL 的過程當中各部分扮演的角色:
也就是客戶端經過 Proxy
訪問 Binder
驅動,而後 Binder
驅動調用 Stub
,而 Stub
中調用咱們的業務邏輯。這裏的 Proxy
和 Stub
用來統一接口函數,Proxy
用來告訴咱們遠程服務中有哪些可用的方法,而具體的業務邏輯則由 Stub
來實現。Binder
的進程通訊就發生在 Proxy
和 Stub
之間。
以上就是 Binder 的工做原理,若有疑問,歡迎評論區交流。