關於BinderPool Binder連線池的愚見
在寫BinderPool之前還記得,AIDL中也可以接受AIDL物件的吧。其實設計者應該也是為此而考慮的。
面對著許多的AIDL需要去完成遠端通訊,難道我們每一個去實現,每一個去呼叫嗎?不不不,這樣程式碼寫起來不簡潔。所以,我們的策略就是通過自己寫一個AIDL檔案去管理眾多AIDL檔案。再用Service去管理這個管理AIDL實現的BinderPool。
這就是BinderPool連線池的思想。
這是不是很想我們在設計模式中見到過的一種的思路呢?
沒錯,就是我們工廠模式,BinderPool在這裡充當工廠。
下面讓我們解析解析原始碼:
首先是三個AIDL檔案
ISecurityCenter.aidl
package com.example.mybinderpool.aidl;
interface ISecurityCenter{
String encrypt(String content);
String decrypt(String password);
}
ICompute.aidl
package com.example.mybinderpool.aidl;
interface ICompute{
int add(int a,int b);
}
IBinderPool.aidl
package com.example.mybinderpool .aidl;
interface IBinderPool{
IBinder queryBinder(int bindercode);
}
我準備做的事情有兩件,跨程序的計算加法以及跨程序的加密字串,接著用IBinderPool這個AIDL去管理它們。
接下來是,頭兩個AIDL的實現:
檔案 ISecurityImpl.java
public class ISecurityImpl extends ISecurityCenter.Stub{
private static char SECRET_CODE= '^';
@Override
public String encrypt (String content) throws RemoteException {
// TODO Auto-generated method stub
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 {
// TODO Auto-generated method stub
return password;
}
}
檔案IComputeImpl.java
public class IComputeImpl extends ICompute.Stub{
@Override
public int add(int a, int b) throws RemoteException {
// TODO Auto-generated method stub
return a + b;
}
}
接下來是BinderPoolService.java的實現
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 void onDestroy(){
super.onDestroy();
}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return mBinderPool;
}
}
上面三段原始碼需要解釋並不多,十分簡單,要注意的是我們在Service中呼叫的Binder是指BinderPool中BinderPoolImpl,這個內部類實際上就是返回查詢之後的Binder。
接下來才是重點,BinderPool的實現:
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 = 1;
private Context context;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;
private BinderPool(Context context){
this.context = context.getApplicationContext();
connectBinderPoolService();
}
//這裡是單例模式,保證只有一個BinderPool
public static BinderPool getInstance(Context context){
if(sInstance == null){
synchronized (BinderPool.class) {
if(sInstance == null){
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
//這裡synchronized的關鍵字是因為,這個行為可能是併發,程序,防止共享資源出現衝突。
private synchronized void connectBinderPoolService(){
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent(context,BinderPoolService.class);
context.bindService(service, mBinderConnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
//呼叫內部類裡面查詢Binder的方法
public IBinder queryBinder(int binderCode){
IBinder binder = null;
try {
if(mBinderPool == null){
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
// TODO: handle exception
e.printStackTrace();
}
return binder;
}
//實現ServiceConnection來繫結Service
private ServiceConnection mBinderConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName arg0) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName classname, IBinder service) {
// TODO Auto-generated method stub
mBinderPool = IBinderPool.Stub.asInterface(service);
try{
//回撥BinderDied方法,為的是給BinderPool設定死亡代理
mBinderPool.asBinder().linkToDeath(mBinderPoolDeath, 0);
}catch(RemoteException e){
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};
private IBinder.DeathRecipient mBinderPoolDeath = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// TODO Auto-generated method stub
//BinderPool斷開後重新連線並且重新繫結
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeath, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
public static class BinderPoolImpl extends IBinderPool.Stub{
public BinderPoolImpl(){
super();
}
//實現了IBinderPool.Stub中查詢功能
@Override
public IBinder queryBinder(int bindercode) throws RemoteException {
// TODO Auto-generated method stub
IBinder binder = null;
switch (bindercode) {
case BINDER_SECURITY:
binder = new ISecurityImpl();
break;
case BINDER_COMPUTE:
binder = new IComputeImpl();
break;
default:
break;
}
return binder;
}
}
}
Binder連線池的核心程式碼就在這裡。
讓我稍微的解析一下:
首先,我們必須保證BinderPool必須是單例,由於遠端通訊可能是併發的,如果宣告兩個BinderPool去同時訪問同一個資源,這樣整個過程不可控。為此我們使用了單例模式。
private synchronized void connectBinderPoolService(){
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent(context,BinderPoolService.class);
context.bindService(service, mBinderConnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
private ServiceConnection mBinderConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName arg0) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName classname, IBinder service) {
// TODO Auto-generated method stub
mBinderPool = IBinderPool.Stub.asInterface(service);
try{
//回撥BinderDied方法,為的是給BinderPool設定死亡代理
mBinderPool.asBinder().linkToDeath(mBinderPoolDeath, 0);
}catch(RemoteException e){
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};
private IBinder.DeathRecipient mBinderPoolDeath = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// TODO Auto-generated method stub
//BinderPool斷開後重新連線並且重新繫結
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeath, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
上面這一段,是為了防止訪問共享資源衝突做出的方案。首先使用synchronized將這個方法上鎖,這個時候這個物件的其他synchronized方法只能等到前一個方法呼叫完畢並釋放鎖之後才能被呼叫。
而CountDownLatch這個構建是Java同步多個或一個任務,強制他們等待由其他任務執行的一組操作執行。
countdown的方法是每一次呼叫,宣告在構造器裡的計數減一,到0之後釋放所有等待任務。
await方法則是,當呼叫counDown()的任務在產生這個呼叫時沒有被阻塞,只有await的呼叫會被呼叫,直到計數變為0。
清楚這兩個方法是怎麼回事之後,就能明白其實就是為了每執行一個任務的時候,另一個任務暫停,當執行完當前任務,才繼續執行。
接下來是MainActivity.java
public class MainActivity extends Activity {
private static final String TAG = "BinderPoolActivity";
private ISecurityCenter mSecurityCenter;
private ICompute mCompute;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
dowork();
}
}).start();
}
private void dowork(){
BinderPool binderPool = BinderPool.getInstance(MainActivity.this);
IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
mSecurityCenter = (ISecurityCenter) ISecurityCenterImpl.asInterface(securityBinder);
Log.e(TAG, "VISIT SECURITY");
String msg = "helloworld";
Log.e(TAG, msg);
try{
String password = mSecurityCenter.encrypt(msg);
Log.e("encrypt",password);
Log.e("decrypt", mSecurityCenter.decrypt(password));
}catch(RemoteException e){
e.printStackTrace();
}
IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
mCompute = IComputeImpl.asInterface(computeBinder);
try{
Log.e("3+5=", Integer.toString(mCompute.add(3, 5)));
}catch(RemoteException e){
e.printStackTrace();
}
}
}
想這種耗時的工作最好放在子執行緒中完成更好。
到這裡Binder連線池就分析完成。