Binder是Android的進程間通信核心,如果看過Android源碼,你會發現源碼中Android的各種核心服務都是通過Binder機制進行相互通信的。在Binder的client部分就是通過代理模式來訪問Server端的。這裏想通過代理模式來詳細介紹Java層Binder。文中會簡單介紹代理模式,詳細介紹Binder機制。(源碼基於6.0.1)
代理模式
意圖
對其他對象提供一種代理以控制對這個對象的訪問。
UML圖
代碼示例
abstract class Subject{
public abstract void operate();
}
class RealSubject{
public void operate(){
System.out.println("real operate");
}
}
class Proxy{
private Subject realSubject;
Proxy(Subject subject){
realSubject = subject;
}
public void operate(){
realSubject.operate();
}
}
public static void main(String[] args){
RealSubject real = new RealSubject();
Proxy proxy = new Proxy(real);
proxy.operate(); //實際上交給了realsubject去操作了。
}
關於代理模式
上面的代碼跟UML圖只是一個例子,並不能完全描述代理模式的。代理模式強調的是一種代理,上面的例子中其實直接用RealSubject就可以了,完全沒必要用Proxy。另外代理與被代理對象並不要求有共同的父類/接口。代理模式有幾種常見的情況:
- 遠程代理(Remote Proxy): 如果某個對象無法實例化,不在同一個地址空間,需要通過編碼來進行通信。比如需要訪問網絡服務器上面的一個對象操作。比如我們接下來需要介紹的Binder。
- 虛代理(Virtual Proxy): 在需要的時候創建大對象,比如超大圖片,我們可以使用一個虛代理代理圖像,在真正需要的時候再去將圖像完全加載出來,在這之前只需要在代理裏面保存圖像的大小,讓它有個佔位就好了。
- 保護代理(Protect Proxy): 需要對對象的某些操作進行隱藏,那麼就可以使用代理對它的接口進行隱藏。
- 智能指引(Smart Reference): 當需要對對象的引用進行計數的時候,可以使用智能指引的代理模式。
Binder
Binder是一個接口形式的IPC。在這裏以我們在應用程序開發過程中使用的.aidl文件聲明生成的接口爲例。使用aidl需要說明的是Android官方文檔上面強調過:
使用aidl的必要條件是你的Service想要被其他客戶端從不同的應用程序爲了IPC而調用,並且想要在你的Service中處理多線程。如果你不需要並行來自不同的應用程序的IPC,那麼你只需要使用實現Binder就好了,如果你不需要處理多線程的並行,那麼你使用Messenger就好了。
這裏只是爲了說明Binder,名稱爲ITestService.aidl:
package com.houzhi.testproject;
interface ITestService{
String getContent();
}
在編譯運行前,Android ide(eclipse/android studio) 會自動生成相關的類,ITestService.Stub, ITestService.Proxy, 而我們通過調用bindService就可以得到Binder的代理。
Intent service; //只是爲了表明類型
ServiceConnection connection = new ServiceConnection(){
public void onServiceConnected(ComponetName name, IBinder service){
ITestService testService = ITestService.stub.asInterface(service);
//testService就是我們得到的代理
}
public void onServiceDisConnected(ComponentName name){
}
}
context.bindService(service,connection);
整個自動生成的類以及Binder, IInterface的UML圖如下:
ITestService.stub.asInterface(service)
的內部就是創建了一個Proxy對象,其實現代碼如下:
public static com.houzhi.testproject.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.houzhi.testproject.IMyAidlInterface))) {
return ((com.houzhi.testproject.IMyAidlInterface) iin);
}
return new com.houzhi.testproject.IMyAidlInterface.Stub.Proxy(obj);
}
上面的代碼需要說明的是obj的queryLocalInterface會返回null, 因爲obj其實是一個BinderProxy類型,而BinderProxy類中,並沒有給DESCRIPTOR添加對應的IInterface。在源碼中具體的實現在native層,native通過jni的方式利用mObject(相當於對應的對象id,BinderProxy的成員變量)創建了一個BinderProxy。將這個BinderProxy返回給客戶端。
從UML圖就已經看出Binder是一個非常典型的代理模式,是一種遠程代理,實際上Proxy代理的是另外一個進程中的Stub對象。內部是將接口函數標記爲對應的ID,然後根據這個ID來標識目前調用的是哪一個函數。由DESCRIPTOR來作爲Token分隔不同的接口調用,另外通過Parcel來寫入函數參數和接受函數返回值(Stub端對應接受參數和寫入結果)。aidl可接受的類型也是普通類型(int,double…),以及Map,List,String,
CharSequence,Parcel,和Binder類型。對應的處理也是不一樣的,不過終歸可以使用基本的方式搞定。
下面是Stub與Proxy關於接口函數實現的具體代碼:
public static abstract class Stub extends android.os.Binder implements com.houzhi.testproject.ITestService {
private static final java.lang.String DESCRIPTOR = "com.houzhi.testproject.ITestService";
//省略了部分代碼
@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_getContent: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getContent();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.houzhi.testproject.ITestService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
//省略了部分代碼
@Override
public java.lang.String getContent() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getContent, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getContent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
Binder底層實現簡述
看完上一節其實就能想到怎麼去實現Binder了,將接口調用請求轉換成數據流的形式,通過int類型的id標識具體是哪一個函數,用token來劃分每一次函數調用請求,按照函數參數順序和類型一個一個地寫入和讀取參數。而在底層通過一個進程通信(Pipe,共享內存)把這些消息發到服務端/客戶端,這樣就可以大致實現了。實際上最底層使用的是Binder驅動,服務端通過IPCThreadState開啓一個線程(IPCThreadState::self()->startThreadPool()),並且將線程放入到線程池中(IPCThreadState::self()->joinThreadPool()),等待接收來自客戶端的請求(talkWithDriver())。而客戶端通過BinderProxy(內部保存了本地BpBinder的指針)的transact,最後訪問Binder(talkWithDriver())。內部使用訪問驅動函數都是ioctl。
ServiceManager
上面大致介紹了服務端,客戶端與Binder驅動通信流程。但是大家有沒有想過,在客戶端需要請求服務端的時候,我怎麼知道我究竟想要請求哪一個服務端呢?ServiceManager。ServiceManager是保存了所有的Service的fd(驅動文件號)。通過請求ServiceManager的getService(String name)
就可以獲取對應的服務。而ServiceManager也是一個服務,它的fd是0,是服務中心管理器。客戶端指定name,通過Binder請求ServiceManager,然後得到客戶端想要的服務BinderProxy,然後就可以請求服務端了。當然創建服務的時候,首先需要通過ServiceManager.addService將服務添加到ServiceManager中。
Binder連接的幾大模塊
Android中應用層常用的幾個重要的模塊都是由Binder聯繫起來的,ActivityManagerService,WindowManagerService,InputManagerService。下面是他們之間的關係圖:
另外還有很多服務,比如說Wifi,NetworkPolicy,Power等等。他們都是在SystemServiceRegistry中註冊的,這個註冊本來在ContextImpl中,現在移到了一個單獨的類SystemServiceRegistry。
代理模式在Binder中的應用分析
代理模式在這裏使用的基本上是天衣無縫了,我覺得這是代理模式的經典使用。我們在客戶端無法直接訪問服務端(因爲跨進程,地址空間都不一致),通過代理模式,能夠讓我們在客戶端感覺像是直接訪問服務端一樣。
如果錯過了太陽時你流了眼淚,那你也要錯過羣星了