一、Binder連接池
之前幾篇文章我已經(jīng)介紹了兩種 IPC 方案:AIDL 和 Messenger。當中,AIDL 也是 Messenger 的底層實現(xiàn),所以對于 AIDL 開發(fā)者需要更為重視一點,這一篇文章也將繼續(xù)對 AIDL 進行更深入的介紹
現(xiàn)在考慮一種情況,假設(shè)在一個設(shè)備上,有一個應(yīng)用作為服務(wù)端存在,作為客戶端的應(yīng)用有十個,而這十個應(yīng)用都需要與服務(wù)端進行通信,且用于與服務(wù)端進行通信的 AIDL 接口各不相同。那么,按照之前介紹的 AIDL 方案,現(xiàn)在服務(wù)端就需要創(chuàng)建十個 Service 來分別與十個客戶端對應(yīng)。這種結(jié)果起來是不可以接受的,因為 Service 作為四大組件之一,創(chuàng)建并運行太多 Service 會使服務(wù)端應(yīng)用看起來太為重量級了 ,也不利于服務(wù)端應(yīng)用的開發(fā)
為了解決這個問題,可以考慮使用 Binder 連接池來管理所有的 AIDL。機制是這樣的,服務(wù)端只創(chuàng)建一個 Service,每個客戶端在請求連接時,帶上自己的唯一標識,服務(wù)端根據(jù)這個唯一標識,返回對應(yīng)的 Binder 給客戶端。這樣,不同的客戶端就可以都綁定到同一個 Service,而獲得的 Binder 對象是唯一的,避免了創(chuàng)建多個 Service 的情況
這里就來模擬這種多個客戶端的 IPC 過程,整個流程是這樣的:有兩個客戶端,一個客戶端傳遞兩個整數(shù)給服務(wù)端進行加法操作,另一個客戶端傳遞兩個整數(shù)給服務(wù)端進行減法操作,所以總的是會有三個不同的應(yīng)用

二、服務(wù)端
首先創(chuàng)建三個需要的 AIDL 接口,IOperation.aidl 用于提供加法操作,ICompute.aidl 用于提供減法操作,IBinderPool.aidl 是一個用于中轉(zhuǎn)的 Binder 對象,含有一個 queryBinder 方法用于接收一個唯一標識,并返回客戶端實際需要的 Binder 對象
package com.czy.binder_pool_server;
interface IOperation {
int add(int parameter1 , int parameter2);
}
package com.czy.binder_pool_server;
interface ICompute {
int subtraction(int parameter1 , int parameter2);
}
package com.czy.binder_pool_server;
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
此外,服務(wù)端還需要有 ICompute.Stub 和 IOperation.Stub 的具體實現(xiàn),因為需要創(chuàng)建這兩個類的子類
/**
* 作者:葉應(yīng)是葉
* 時間:2018/3/23 21:17
* 描述:https://github.com/leavesC
*/
public class IOperationImpl extends IOperation.Stub {
@Override
public int add(int parameter1, int parameter2) throws RemoteException {
return parameter1 + parameter2;
}
}
/**
* 作者:葉應(yīng)是葉
* 時間:2018/3/23 21:13
* 描述:https://github.com/leavesC
*/
public class IComputeImpl extends ICompute.Stub {
@Override
public int subtraction(int parameter1, int parameter2) throws RemoteException {
return parameter1 - parameter2;
}
}
之后就是來創(chuàng)建那唯一的一個 Service 了。Service 直接返回的是 BinderPoolImpl 對象,而 BinderPoolImpl 對象的 queryBinder 可以根據(jù)傳入的參數(shù)再返回對應(yīng)的 Binder 對象,即進行中轉(zhuǎn)轉(zhuǎn)發(fā),從而使客戶端得到真實想要的 Binder 對象
/**
* 作者:葉應(yīng)是葉
* 時間:2018/3/23 21:55
* 描述:https://github.com/leavesC
*/
public class BinderPoolService extends Service {
private class BinderPoolImpl extends IBinderPool.Stub {
@Override
public IBinder queryBinder(int binderId) {
switch (binderId) {
case 100: {
return new IOperationImpl();
}
case 200: {
return new IComputeImpl();
}
}
return null;
}
}
private Binder binderPool;
public BinderPoolService() {
binderPool = new BinderPoolImpl();
}
@Override
public IBinder onBind(Intent intent) {
return binderPool;
}
}
三、客戶端
本客戶端請求服務(wù)端進行加法操作,因此需要把 IOperation.aidl 文件和 IBinderPool.aidl 文件拷貝過來
與服務(wù)端的綁定操作與之前的文章介紹的 AIDL 機制大致相同,區(qū)別只在于在 onServiceConnected 中需要調(diào)用 queryBinder 方法獲取真實的 Binder 對象
/**
* 作者:葉應(yīng)是葉
* 時間:2018/3/23 22:32
* 描述:https://github.com/leavesC
*/
public class MainActivity extends AppCompatActivity {
private IOperation operation;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
IBinderPool binderPool = IBinderPool.Stub.asInterface(service);
//本客戶端的唯一標識是 100
//獲取真實的 Binder 對象
operation = IOperation.Stub.asInterface(binderPool.queryBinder(100));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
operation = null;
bindService();
}
};
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (operation != null) {
try {
Log.e(TAG, "4+2 加法:" + operation.add(4, 2));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (operation != null) {
unbindService(serviceConnection);
}
}
private void bindService() {
Intent intent = new Intent();
intent.setClassName("com.czy.binder_pool_server", "com.czy.binder_pool_server.BinderPoolService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
另外一個客戶端的操作也類似,這里不再贅述,具體的代碼可以直接看我傳到 GitHub 上的示例代碼
運行結(jié)果如下所示

這樣,以后每增加一個客戶端,就可以再為其指定一個唯一標識,然后在服務(wù)端中返回對應(yīng)的 Binder 對象即可,從而避免了創(chuàng)建多個 Service 的情況,極大的提高了開發(fā)效率
本系列關(guān)于 Android 平臺下的 IPC 機制的文章到這里目前也就結(jié)束,之后如果還有其它值得介紹的內(nèi)容的話,我也會繼續(xù)寫下一篇
這里提供本系列文章所有的 IPC 示例代碼:IPCSamples