1. 程式人生 > >關於BinderPool Binder連線池的愚見

關於BinderPool Binder連線池的愚見

在寫BinderPool之前還記得,AIDL中也可以接受AIDL物件的吧。其實設計者應該也是為此而考慮的。
面對著許多的AIDL需要去完成遠端通訊,難道我們每一個去實現,每一個去呼叫嗎?不不不,這樣程式碼寫起來不簡潔。所以,我們的策略就是通過自己寫一個AIDL檔案去管理眾多AIDL檔案。再用Service去管理這個管理AIDL實現的BinderPool。
這就是BinderPool連線池的思想。

BinderPool原理圖

這是不是很想我們在設計模式中見到過的一種的思路呢?
沒錯,就是我們工廠模式,BinderPool在這裡充當工廠。

下面讓我們解析解析原始碼:
首先是三個AIDL檔案

ISecurityCenter.aidl

package com.example.mybinderpool.aidl;

interface ISecurityCenter{
    String encrypt(String content);
    String decrypt(String password);
}

ICompute.aidl

package com.example.mybinderpool.aidl;

interface ICompute{
    int add(int a,int b);
}

IBinderPool.aidl

package com.example.mybinderpool
.aidl; interface IBinderPool{ IBinder queryBinder(int bindercode); }

我準備做的事情有兩件,跨程序的計算加法以及跨程序的加密字串,接著用IBinderPool這個AIDL去管理它們。

接下來是,頭兩個AIDL的實現:

檔案 ISecurityImpl.java

public class ISecurityImpl extends ISecurityCenter.Stub{
    private static char SECRET_CODE= '^';

    @Override
    public String encrypt
(String content) throws RemoteException { // TODO Auto-generated method stub char[] chars = content.toCharArray(); for(int i = 0;i < chars.length;i++){ chars[i] ^= SECRET_CODE; } return new String(chars); } @Override public String decrypt(String password) throws RemoteException { // TODO Auto-generated method stub return password; } }

檔案IComputeImpl.java

public class IComputeImpl extends ICompute.Stub{

    @Override
    public int add(int a, int b) throws RemoteException {
        // TODO Auto-generated method stub
        return a + b;
    }

}

接下來是BinderPoolService.java的實現

public class BinderPoolService extends Service{

    private static final String TAG = "BinderPoolService";

    private Binder mBinderPool = new BinderPool.BinderPoolImpl();

    @Override
    public void onCreate(){
        super.onCreate();
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
    }


    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return mBinderPool;
    }

}

上面三段原始碼需要解釋並不多,十分簡單,要注意的是我們在Service中呼叫的Binder是指BinderPool中BinderPoolImpl,這個內部類實際上就是返回查詢之後的Binder。

接下來才是重點,BinderPool的實現:

public class BinderPool {
    private static final String TAG = "BinderPool";
    public static final int BINDER_NONE = -1;
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY = 1;

    private Context context;
    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private CountDownLatch mConnectBinderPoolCountDownLatch;

    private BinderPool(Context context){
        this.context = context.getApplicationContext();
        connectBinderPoolService();
    }
    //這裡是單例模式,保證只有一個BinderPool
    public static BinderPool getInstance(Context context){
        if(sInstance == null){
            synchronized (BinderPool.class) {
                if(sInstance == null){
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    //這裡synchronized的關鍵字是因為,這個行為可能是併發,程序,防止共享資源出現衝突。
    private synchronized void connectBinderPoolService(){
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(context,BinderPoolService.class);
        context.bindService(service, mBinderConnection, Context.BIND_AUTO_CREATE);
        try {
            mConnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

    //呼叫內部類裡面查詢Binder的方法
    public IBinder queryBinder(int binderCode){
        IBinder binder = null;
        try {
            if(mBinderPool == null){
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return binder;
    }

    //實現ServiceConnection來繫結Service
    private ServiceConnection mBinderConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName classname, IBinder service) {
            // TODO Auto-generated method stub
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try{
                //回撥BinderDied方法,為的是給BinderPool設定死亡代理
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeath, 0);
            }catch(RemoteException e){
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();

        }
    };

    private IBinder.DeathRecipient mBinderPoolDeath = new IBinder.DeathRecipient() {

        @Override
        public void binderDied() {
            // TODO Auto-generated method stub
            //BinderPool斷開後重新連線並且重新繫結
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeath, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };

    public static class BinderPoolImpl extends IBinderPool.Stub{

        public BinderPoolImpl(){
            super();
        }


        //實現了IBinderPool.Stub中查詢功能
        @Override
        public IBinder queryBinder(int bindercode) throws RemoteException {
            // TODO Auto-generated method stub
            IBinder binder = null;
            switch (bindercode) {
            case BINDER_SECURITY:
                binder = new ISecurityImpl();
                break;

            case BINDER_COMPUTE:
                binder = new IComputeImpl();
                break;
            default:
                break;
            }

            return binder;
        }

    }


}

Binder連線池的核心程式碼就在這裡。
讓我稍微的解析一下:
首先,我們必須保證BinderPool必須是單例,由於遠端通訊可能是併發的,如果宣告兩個BinderPool去同時訪問同一個資源,這樣整個過程不可控。為此我們使用了單例模式。

private synchronized void connectBinderPoolService(){
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(context,BinderPoolService.class);
        context.bindService(service, mBinderConnection, Context.BIND_AUTO_CREATE);
        try {
            mConnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

    private ServiceConnection mBinderConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName classname, IBinder service) {
            // TODO Auto-generated method stub
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try{
                //回撥BinderDied方法,為的是給BinderPool設定死亡代理
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeath, 0);
            }catch(RemoteException e){
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();

        }
    };

    private IBinder.DeathRecipient mBinderPoolDeath = new IBinder.DeathRecipient() {

        @Override
        public void binderDied() {
            // TODO Auto-generated method stub
            //BinderPool斷開後重新連線並且重新繫結
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeath, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };

上面這一段,是為了防止訪問共享資源衝突做出的方案。首先使用synchronized將這個方法上鎖,這個時候這個物件的其他synchronized方法只能等到前一個方法呼叫完畢並釋放鎖之後才能被呼叫。

而CountDownLatch這個構建是Java同步多個或一個任務,強制他們等待由其他任務執行的一組操作執行。
countdown的方法是每一次呼叫,宣告在構造器裡的計數減一,到0之後釋放所有等待任務。
await方法則是,當呼叫counDown()的任務在產生這個呼叫時沒有被阻塞,只有await的呼叫會被呼叫,直到計數變為0。

清楚這兩個方法是怎麼回事之後,就能明白其實就是為了每執行一個任務的時候,另一個任務暫停,當執行完當前任務,才繼續執行。

接下來是MainActivity.java

public class MainActivity extends Activity {
    private static final String TAG = "BinderPoolActivity";

    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                dowork();
            }
        }).start();

    }

    private void dowork(){
        BinderPool binderPool = BinderPool.getInstance(MainActivity.this);
        IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        mSecurityCenter = (ISecurityCenter) ISecurityCenterImpl.asInterface(securityBinder);
        Log.e(TAG, "VISIT SECURITY");
        String msg = "helloworld";
        Log.e(TAG, msg);

        try{
            String password = mSecurityCenter.encrypt(msg);
            Log.e("encrypt",password);
            Log.e("decrypt", mSecurityCenter.decrypt(password));
        }catch(RemoteException e){
            e.printStackTrace();
        }

        IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        mCompute = IComputeImpl.asInterface(computeBinder);
        try{

            Log.e("3+5=", Integer.toString(mCompute.add(3, 5)));
        }catch(RemoteException e){
            e.printStackTrace();
        }
    }


}

想這種耗時的工作最好放在子執行緒中完成更好。

到這裡Binder連線池就分析完成。