導(dǎo)語
最近在學(xué)習(xí)微服務(wù)相關(guān)知識,突然想到:微服務(wù)的思想雖然是在server端的場景下提出來的,但是無論是server,還是移動端,思想是相通的,移動端也會有多服務(wù)的場景,就同樣面臨多服務(wù)需要整合治理的問題。
1. 背景
隨著公司業(yè)務(wù)的發(fā)展,項目規(guī)模會越來越大,可能會遇到多多服務(wù)IPC的場景,有很多模塊,而每一個模塊都需要和服務(wù)端通訊,那么我們也要為每一個模塊創(chuàng)建特定的AIDL文件,那么服務(wù)端service也會產(chǎn)生很多個。作為四大組件之一,過多使用也會造成性能資源消耗。所以我們可以設(shè)計只有一個Service,對于不同可客戶端我們只是去返回一個不同的Binder即可,這樣就避免了創(chuàng)建了大量的Service。
2. AIDL
模擬Binder連接池, 使用兩個簡單的AIDL接口與實現(xiàn), 一個是加解密, 一個是加法。
package com.mantoudev.binderpooldemo;
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String pwd);
}
加密和解密的實現(xiàn), 這里使用簡單的異或運算處理。
public class SecurityCenterImpl extends ISecurityCenter.Stub {
private static final char CODE_SECRET = 'z';
@Override public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] ^= CODE_SECRET;
}
return new String(chars);
}
@Override public String decrypt(String password) throws RemoteException {
return encrypt(password);
}
}
求和的AIDL接口:
package com.mantoudev.binderpooldemo;
interface ICompute {
int add(int a, int b);
}
求和的實現(xiàn):
public class ComputeImpl extends ICompute.Stub {
@Override public int add(int a, int b) throws RemoteException {
return a + b;
}
}
Binder連接池通過ID查找Bidner, 查詢并返回匹配的Binder:
package com.mantoudev.binderpooldemo;
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
3. Binder 連接池
Service服務(wù)通過Binder連接池動態(tài)選擇Binder請求:
private Binder mBinderPool = new BinderPool.BinderPoolImpl();
@Override public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
return mBinderPool;
}
Binder連接池的具體實現(xiàn), 創(chuàng)建BinderPool單例, 連接服務(wù):
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService(); // 連接服務(wù)
}
public static BinderPool getInstance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
綁定服務(wù), 通過CountDownLatch類, 把異步操作轉(zhuǎn)換為同步操作, 防止綁定沖突,對通過CountDownLatch類不了解的請自行百度:
private synchronized void connectBinderPoolService() {
mCountDownLatch = new CountDownLatch(1); // 只保持一個綁定服務(wù)
Intent service = new Intent(mContext, BinderPoolService.class);
mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
通過DeathRecipient處理Binder連接池死亡重聯(lián)機制:
很多人喜歡在servierConnection的onServiceDisconnected()回調(diào)中做重連處理,這里我簡單介紹下DeathRecipient
DeathRecipient
Binder有可以讓對端的進程得到意外退出通知的機制:Link-To-Death。我這里以我們這里Service被通知Client意外退出的情況為例,實現(xiàn)的方法如下:
Client傳遞一個Binder對象給Service,此Binder對象與Client的進程關(guān)聯(lián);
在Sevice中接受到這個Binder對象,并且使用binder.linkToDeath(),注冊一個DeathRecipient回調(diào);
實現(xiàn)DeathRecipient。當Client意外退出的時候,DeathRecipient.binderDied()將被回調(diào),我們可以在這里釋放相關(guān)的資源。
// 失效重聯(lián)機制, 當Binder死亡時, 重新連接
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override public void binderDied() {
Log.e(TAG, "binderDied@DeathRecipient()");
mBinderPool.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
// Binder的服務(wù)連接
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected@ServiceConnection()");
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mCountDownLatch.countDown();
}
@Override public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected@ServiceConnection()");
}
};
通過ID連接不同的Binder請求.
public IBinder queryBinder(int binderCode) {
Log.e(TAG, "queryBinder---BinderCode:" + binderCode );
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
Binder連接池AIDL的具體實現(xiàn), 通過ID選擇Binder.
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_COMPUTE:
binder = new ComputeImpl();
break;
case BINDER_SECURITY_CENTER:
binder = new SecurityCenterImpl();
break;
default:
break;
}
return binder;
}
}
1.4 Binder客戶端
我們在子線程進行耗時操作:
//測試
private void doTest(){
new Thread(new Runnable() {
@Override public void run(){
encryptTest();
}
}).start();
}
private void encryptTest() {
BinderPool binderPool = BinderPool.getInstance(getApplicationContext());
IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
mISecurityCenter = SecurityCenterImpl.asInterface(securityBinder);
String msg = "BinderPool Test Msg!~~~";
try {
String encryptMsg = mISecurityCenter.encrypt(msg);
Log.e(TAG, "加密了: " + encryptMsg);
String decryptMsg = mISecurityCenter.decrypt(encryptMsg);
Log.e(TAG, "解密了: " + decryptMsg);
Message hm = new Message();
hm.what = 0;
hm.obj = encryptMsg + "\n" + decryptMsg;
mHandler.sendMessage(hm);
} catch (RemoteException e) {
e.printStackTrace();
}
}
1.5 總結(jié)
使用BinderPool我們就只需要建立一個Service就足夠了。當我們添加一個AIDL接口的時候只需要在BinderPool中添加一個id,然后根據(jù)這個id,在BinderPoolImpl中創(chuàng)建一個對應(yīng)的Binder對象即可。這樣就很大程度上簡化了我們的工作,同時也節(jié)省了系統(tǒng)資源開銷,要知道,在移動端,資源、性能的要求要做到更高。