天天看點

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

  1. Bindler 連接配接池

          這裡引用了任玉剛老師的<Android 開發藝術探索> 中的 Binder 連接配接池概念,本來想自己總結的,想了下其實任玉剛老師總結的挺好的就照搬過來了,一個字“懶”啊!

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

。上一篇咱們介紹到了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();
    }

}           

                     看起來是不是特簡單

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

                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 位址

繼續閱讀