1. 程式人生 > >AIDL連線池的實現

AIDL連線池的實現

參考《Android開發藝術探索》學習一下AIDL的連線池實現

回顧一下AIDL使用的大致流程:首先建立一個Service和一個AIDL介面,接著建立一個類繼承自AIDL介面中的Stub類並實現Stub中的抽象方法,在Service的onBind方法中返回這個類的物件,然後客戶端就可以繫結服務端Service,建立連線後就可以訪問遠端服務端的方法了。

現在考慮一種情況:公司的專案越來越龐大了,現在有10個不同的業務模組都需要使用AIDL來進行程序間通訊,那我們該怎麼處理呢?也許你會說:“就按照AIDL的實現方式一個個來吧”,這是可以的,如果用這種方法,首先我們需要建立10個Service,這好像有點多啊!如果有100個地方需要用到AIDL呢,先建立100個Service?到這裡,讀者應該明白問題所在了。隨著AIDL數量的增加,我們不能無限制地增加Service,Service是四大元件之一,本身就是一種系統資源。而且太多的Service會使得我們的應用看起來很重量級,因為正在執行的Service可以在應用詳情頁看到,當我們的應用詳情顯示有10個服務正在執行時,這看起來並不是什麼好事。針對上述問題,我們需要減少Service的數量,將所有的AIDL放在同一個Service中去管理。在這種模式下,整個工作機制是這樣的:每個業務模組建立自己的AIDL介面並實現此介面,這個時候不同業務模組之間是不能有耦合的,所有實現細節我們要單獨開來,然後向服務端提供自己的唯一標識和其對應的Binder物件;對於服務端來說,只需要一個Service就可以了,服務端提供一個queryBinder介面,這個介面能夠根據業務模組的特徵來返回相應的Binder物件給它們,不同的業務模組拿到所需的Binder物件後就可以進行遠端方法呼叫了。由此可見,Binder連線池的主要作用就是將每個業務模組的Binder請求統一轉發到遠端Service中去執行,從而避免了重複建立Service的過程。
1542631800(1).jpg

第一步,假設目前需求是需要兩個AIDL介面,來實現加密解密和計算的功能,那麼先新建ISecurityCenter.aidl 和 ICompute.aidl檔案以及它們的實現類:

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
} 
interface ICompute {
    int add(int a, int b);
}

public class SecurityCenterImpl extends ISecurityCenter.Stub {
    private static final char SECRET_CODE = '^';
    @Override
    public String encrypt(String content) throws RemoteException {
        // TODO 功能實現略
        return "加密後的字串";
    }
    @Override
    public String decrypt(String password) throws RemoteException {
        // TODO 功能實現略
        return "解密後的字串";
    }
}

public class ComputeImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}

第二步,為Binder連線池建立IBinderPool.aidl檔案並實現服務:

public class BinderPoolService extends Service {
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;
 
    private Binder mBinderPool = new BinderPoolImpl();
 
    public static class BinderPoolImpl extends IBinderPool.Stub {
        @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;
        }
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
    }
    @Override
    public IBinder onBind(Intent intent) {
        return mBinderPool;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

public class BinderPoolService extends Service {
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;
 
    private Binder mBinderPool = new BinderPoolImpl();
 
    public static class BinderPoolImpl extends IBinderPool.Stub {
        @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;
        }
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
    }
    @Override
    public IBinder onBind(Intent intent) {
        return mBinderPool;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

第三步,重要,新建一個BinderPool類用於專門處理Binder連線池的繫結Service和獲取對應Binder物件:

public class BinderPool {
 
    // 單例
    private static volatile BinderPool sInstance;
    public static BinderPool getInsance(Context context) {
        if (sInstance == null) {
            synchronized (BinderPool.class) {
                if (sInstance == null) {
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }
 
    private Context mContext;
    private IBinderPool mBinderPool;
    // 一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待(非同步轉同步)
    private CountDownLatch mConnectBinderPoolCountDownLatch;
 
    private BinderPool(Context context) {
        mContext = context.getApplicationContext();
        connectBinderPoolService();
    }
 
    // 繫結服務
    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();
        }
    }
 
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 連線上服務後,獲取服務裡遠端提供的Binder mBinderPool物件,它是前面的IBinderPool介面
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                // linkToDeath可以給Binder設定一個死亡代理
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
 
    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            // 重新繫結服務
            connectBinderPoolService();
        }
    };
 
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        try {
            if (mBinderPool != null) {
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }
}

1、BinderPool的實現方式是一個單例,並在建構函式中實現了Service的繫結
2、Service的繫結過程中使用了CountDownLatch來進行同步執行,以確保客戶端在執行呼叫之前已經繫結好服務端
3、繫結Service完成後,獲得一個mBinderPool物件,併為其設定一個死亡代理,使在意外斷開後能重新繫結
4、對外提供queryBinder方法,通過約定的code呼叫mBinderPool 物件的queryBinder方法返回相應用Binder物件

第四步、使用

private void doWork() {
    BinderPool binderPool = BinderPool.getInsance(MainActivity.this);
 
    IBinder securityBinder = binderPool.queryBinder(BinderPoolService.BINDER_SECURITY_CENTER);
    mSecurityCenter = SecurityCenterImpl.asInterface(securityBinder);
    try {
        String msg = "hello ffo";
        String password = mSecurityCenter.encrypt(msg);
        String originalPassword  = mSecurityCenter.decrypt(password);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
 
    IBinder computeBinder = binderPool.queryBinder(BinderPoolService.BINDER_COMPUTE);
    mCompute = ComputeImpl.asInterface(computeBinder);
    try {
        int reuslt = mCompute.add(6, 8);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

有了BinderPool可以大大方便日常的開發工作,比如如果有一個新的業務模組需要新增新的AIDL,那麼在它實現了自己的AIDL介面後,只需要修改BinderPoolImpl中的queryBinder方法,給自己新增一個新的binderCode並返回對應的Binder物件即可,不需要做其他修改,也不需要建立新的Service。由此可見,BinderPool能夠極大地提高AIDL的開發效率,並且可以避免大量的Service建立,因此,建議在AIDL開發工作中引入BinderPool
機制。