Android源碼代理模式---Binder

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圖如下:
binder

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。下面是他們之間的關係圖:
binder connect

另外還有很多服務,比如說Wifi,NetworkPolicy,Power等等。他們都是在SystemServiceRegistry中註冊的,這個註冊本來在ContextImpl中,現在移到了一個單獨的類SystemServiceRegistry。

代理模式在Binder中的應用分析

代理模式在這裏使用的基本上是天衣無縫了,我覺得這是代理模式的經典使用。我們在客戶端無法直接訪問服務端(因爲跨進程,地址空間都不一致),通過代理模式,能夠讓我們在客戶端感覺像是直接訪問服務端一樣。


如果錯過了太陽時你流了眼淚,那你也要錯過羣星了

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