Android 進程間通信之 AIDL (二)高級用法及建議

  1. Bindler 連接池

          這裏引用了任玉剛老師的<Android 開發藝術探索> 中的 Binder 連接池概念,本來想自己總結的,想了下其實任玉剛老師總結的挺好的就照搬過來了,一個字“懶”啊!偷笑。上一篇咱們介紹到了Android AIDL 基本用法還沒有看過的同學可以先去溫習下。

           現在假設有 N 個不同的業務模塊需要使用 AIDL 來進行進程間通信,那我們該怎麼處理勒?或許你會說“就按照  AIDL 的實現方式一個一個來實現吧”你有沒有想過這樣會創建 N 個 Service,當用戶打開手機設置查看正在運行的進程,發現某某某 APP Service TMD 有10 個 甚至更多。他的第一反應也許是該卸載了,我們不可能無限制的創建 Service ,Service 是四大組件之一,是需要消耗內存的。而且太多的話會使我們的 APP 看起來特別很重量級。爲了避免這種問題發生,咱們可以引用 Binder 連接池來解決這個問題,下面對 Bindler 連接池的代碼實現做一下說明。首先,爲了說明問題,我們提供了兩個 AIDL 接口 ISecurityCenter 和 Icompute 來模擬上面提到的多模塊應用的情況。

        一. 創建 ISecurityCenter ,Icompute 接口。

package android.t01.com.common;

// Declare any non-default types here with import statements

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}
package android.t01.com.common;

// Declare any non-default types here with import statements

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

         二. 實現 AIDL 接口。

              爲了便於理解這裏簡單處理

            

public class SecurityCenterImpl extends ISecurityCenter.Stub {

    private static final char SECRET_CODE = '^';

    @Override
    public String encrypt(String content) throws RemoteException {
        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 {
        return encrypt(password);
    }

}




public class ComputeImpl extends ICompute.Stub {

    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }

}

        三. 服務端和 Binder 連接池的準備工作。

             不知道有沒有同學發現,咱們並沒有爲每個 Moudle AIDL 去單獨創建 Service。

              1. 首先,創建 連接池接口 IBindlerPool.aidl。

                

interface IBinderPool {
    /**
     * @param binderCode, the unique token of specific Binder<br/>
     * @return specific Binder who's token is binderCode.
     */
    IBinder queryBinder(int binderCode);
}

                2. 實現 連接池接口

    

    public static class BinderPoolImpl extends IBinderPool.Stub {

        public BinderPoolImpl() {
            super();
        }

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
                case BINDER_SECURITY_CENTER: {
                    binder = new SecurityCenterImpl();
                    break;
                }
                case BINDER_COMPUTE: {
                    binder = new ComputeImpl();
                    break;
                }
                default:
                    break;
            }

            return binder;
        }
    }

                3. 創建服務端的 Servicer.

        

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 IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return mBinderPool;
    }

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

}

                     看起來是不是特簡單大笑

                4. Bindler 連接池的具體實現。

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_CENTER = 1;


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

    private BinderPool(Context context) {
        mContext = context.getApplicationContext();
        connectBinderPoolService();
    }

    public static BinderPool getInsance(Context context) {
        if (sInstance == null) {
            synchronized (BinderPool.class) {
                if (sInstance == null) {
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    private synchronized void connectBinderPoolService() {
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(service, mBinderPoolConnection,
                Context.BIND_AUTO_CREATE);
        try {
            mConnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    /**
     * query binder by binderCode from binder pool
     *
     * @param binderCode
     *            the unique token of binder
     * @return binder who's token is binderCode<br>
     *         return null when not found or BinderPoolService died.
     */
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        try {
            if (mBinderPool != null) {
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // ignored.
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }
    };


    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.w(TAG, "binder died.");
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };


    public static class BinderPoolImpl extends IBinderPool.Stub {

        public BinderPoolImpl() {
            super();
        }

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
                case BINDER_SECURITY_CENTER: {
                    binder = new SecurityCenterImpl();
                    break;
                }
                case BINDER_COMPUTE: {
                    binder = new ComputeImpl();
                    break;
                }
                default:
                    break;
            }

            return binder;
        }
    }
}
  • Bindler  連接池的具體實現分析就完了,它的好處是顯然易見的。

                5. 客服端操作。

   

private void doWork() {
        BinderPool binderPool = BinderPool.getInsance(MyBookActivity.this);
        IBinder securityBinder = binderPool
                .queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        ;
        mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
                .asInterface(securityBinder);
        Log.d(TAG, "visit ISecurityCenter");
        String msg = "helloworld-安卓";
        System.out.println("content:" + msg);
        try {
            String password = mSecurityCenter.encrypt(msg);
            System.out.println("encrypt:" + password);
            System.out.println("decrypt:" + mSecurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        Log.d(TAG, "visit ICompute");
        IBinder computeBinder = binderPool
                .queryBinder(BinderPool.BINDER_COMPUTE);
        ;
        ICompute mCompute = ComputeImpl.asInterface(computeBinder);
        try {
            System.out.println("3+5=" + mCompute.add(3, 5));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
            

        注意:這裏需要額外的說明下,爲什麼需要在子線程中去執行了?這是因爲在 Bindler 連接池的實現中,我們需要通過 CountDownLatch 將 bindService 這一異步操作轉換爲同步操作,這就意味着他是需要耗時的並且 Bindler 中的方法調用也是耗時的,因此不建議放在主線程。


    總結

        有了 BinderPool 可以大大方便和提高日常的開發工作,比如如果有一個新的業務模塊 B 需要添加新的 AIDL,那麼在它實現了自己的 AIDL 接口後,只需要修改 BinderPoolImpl 中的 queryBinder 方法,給自己新添加一個 binderCode 並返回對應的 Binder 對象即可!


    建議

    
名稱優點缺點適用場景
 Bundle簡單易用只能傳輸 Bundle 支持的數據類型   四大組件的進程間
文件共享簡單易用不適合高併發場景,並且無法做到進程間的即時通信無併發訪問情形,交換簡單的數據實時性不高的場景
AIDL功能強大,支持一對多併發,支持實時通信

使用稍複雜,需要處理好線程同步

一對多通信且有 RPC 需求
Messenger功能一般,支持一對多串行通信,支持實時通信不能很好處理高併發,只能傳輸 Bundle 數據低併發一對多
ContentProvider在數據源訪問方面功能強大,支持一對多併發數據共享,可通過 Call 方法擴展其它操作可以理解爲受約束的 AIDL一對多的進程間的數據共享
Socket功能強大,可以通過網絡傳輸字節流,支持一對多併發實時通信實現細節稍微有點煩瑣,不支持直接的 RPC網絡數據交換

   

Demo 地址



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