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的過程。
第一步,假設目前需求是需要兩個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
機制。