您的位置:首页 > 移动开发 > Android开发

Binder连接池

2017-01-09 17:27 309 查看

概述

有下面这样一种应用场景:一个Android应用有10个功能模块,供主应用通过aidl调用。按照aidl的标准使用方法,就是创建10个aidl接口并写10个类实现他们,然后创建10个服务,在这10个服务的onBind里面返回接口的实现。那么问题来了,有必要为每个模块都创建一个服务吗?如果有100个模块呢?也要这么创建100个服务吗?

答案是否定的,解决这样一个场景的方法就是Binder连接池。

Binder连接池的结构如下图所示:



BindPoolService是一个通过标准方式建立的远程服务,它只有一个方法:query。这个方法根据传递进去的参数返回对应的IBinder,客户端就可以利用这个IBinder调用不同模块的方法。在这种模式下,系统中只有一个服务在后台运行。

下面就来实现这样一种解决方案。

实现服务端

新建两个模块的aidl接口,并实现之。

// IAddCenter.aidl
package com.example.timothy.binderpoolservice;

interface IAddCenter {
int add(int a, int b);
}

// ISubCenter.aidl
package com.example.timothy.binderpoolservice;

interface ISubCenter {
int sub(int a, int b);
}

// AddCenterImpl.java
public class AddCenterImpl extends IAddCenter.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}

// SubCenterImpl.java
public class SubCenterImpl extends ISubCenter.Stub {
@Override
public int sub(int a, int b) throws RemoteException {
return a - b;
}
}


定义IBinderPool接口并实现之。

// IBinderPool.aidl
package com.example.timothy.binderpoolservice;

interface IBinderPool {
IBinder query(int code);
}

// BinderPoolImpl.java
public class BinderPoolImpl extends IBinderPool.Stub {
private static final String TAG = "BinderPoolService";

@Override
public IBinder query(int code) throws RemoteException {
Log.d(TAG, "query() is called, code = " + code);
IBinder binder = null;
switch (code) {
case BinderPoolService.CODE_ADD:
binder = new AddCenterImpl();
break;
case BinderPoolService.CODE_SUB:
binder = new SubCenterImpl();
break;
default:
break;
}
return binder;
}
}


query方法就是根据请求功能模块的代码,返回相应模块的实现。

实现BinderPoolService。

// BinderPoolService.java
public class BinderPoolService extends Service {
private static final String TAG = "BinderPoolService";

// 服务代码
public static final int CODE_ADD = 0;
public static final int CODE_SUB = 1;

private BinderPoolImpl mBinderPoolImp = new BinderPoolImpl();

@Override
public IBinder onBind(Intent intent) {
return mBinderPoolImp;
}
}


配置服务

<service
android:name=".BinderPoolService"
android:enabled="true"
android:exported="true"
android:process=":remote">
<intent-filter>
<action android:name="com.example.timothy.binderpoolservice" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>


实现客户端

复制AIDL接口定义文件

客户端首先要做的是将AIDL文件全部从服务端复制过来。

实现一个BinderPool的管理器

为了操作方便,将所有的BinderPool操作放在同一个类里面。这个类的大体结构和标准的AIDL客户端基本相同。首先要有一个IBinderPool类型的私有变量,然后定义一个ServiceConnection类型的私有变量,并在它的onServiceConnected 方法里面给IBinderPool类型的变量赋值,然后定义一个bindBinderPoolService函数,在里面bingService。完整的代码如下:

public class BinderPoolOp {
private static final String TAG = "BinderPoolClient";

private Context mContext;
private static BinderPoolOp sInstance;
private IBinderPool mBinderPool;
private CountDownLatch mCountDownLatch;

private BinderPoolOp(Context context){
this.mContext = context;
bindBinderPoolService();
}

public static BinderPoolOp getInstance(Context context){
if(sInstance == null){
sInstance = new BinderPoolOp(context);
}
return sInstance;
}

private ServiceConnection mServiceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected() is called");
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(this, 0);
mBinderPool = null;
bindBinderPoolService();
}
}, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mCountDownLatch.countDown();
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

private void bindBinderPoolService(){
Log.d(TAG, "bindService() is called");
mCountDownLatch = new CountDownLatch(1);
Intent intent = new Intent("com.example.timothy.binderpoolservice");
intent.setPackage("com.example.timothy.binderpoolservice");
Log.d(TAG, "start to bind service");
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "finish to bind service");
}

public IBinder query(int code){
Log.d(TAG, "query() is called, code = " + code);
IBinder binder = null;
try {
binder = mBinderPool.query(code);
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
}


调用BinderPool管理器

在Activity里面新建一个线程,在线程里面调用管理器的query方法,就能获取相应的模块的IBinder对象,然后就能通过这个对象来调用模块的方法。代码如下:

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
doWork();
}
}).start();
}

public void doWork(){
BinderPoolOp mBinderPoolOp = BinderPoolOp.getInstance(MainActivity.this);
int ret1 = 0, ret2 = 0;

IAddCenter adder = IAddCenter.Stub.asInterface(mBinderPoolOp.query(CODE_ADD));
try {
ret1 = adder.add(5, 8);
} catch (RemoteException e) {
e.printStackTrace();
}

ISubCenter subber = ISubCenter.Stub.asInterface(mBinderPoolOp.query(CODE_SUB));

try {
ret2 = subber.sub(5, 8);
} catch (RemoteException e) {
e.printStackTrace();
}
Log.d(TAG, "add ret is :" + ret1);
Log.d(TAG, "sub ret is :" + ret2);
}


运行结果

该程序的运行结果如下:

01-09 08:12:52.699 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: bindService() is called
01-09 08:12:52.699 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: start to bind service
01-09 08:12:52.715 3852-3852/com.example.timothy.binderpoolclient D/BInderPoolClient: onServiceConnected() is called
01-09 08:12:52.717 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: finish to bind service
01-09 08:12:52.717 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: query() is called, code = 0
01-09 08:12:52.721 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: query() is called, code = 1
01-09 08:12:52.727 3852-3882/com.example.timothy.binderpoolclient D/BinderPoolClient: add ret is :13
01-09 08:12:52.727 3852-3882/com.example.timothy.binderpoolclient D/BinderPoolClient: sub ret is :-3


这里有个点需要注意一下:为什么在activity里面要用新建线程来调用BinderPool管理器的方法?

我们可以看到在doWork方法里面,会创建一个BinderPool管理器,而在BinderPool管理器的构造函数里面,调用了bindBinderPoolService方法,这个方法会调用Context的bindService方法。bingService方法是一个异步的方法,它返回了并不代表服务已经绑定上,mBinderPool已经可以用了。只有当ServiceConnection对象的onServiceConnected回调被调用的时候,才代表已经绑定上了。所以,用了一个CountDownLatch做同步。关于耗时这点,我们可以从运行结果中看出:

01-09 08:12:52.699 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: start to bind service
01-09 08:12:52.715 3852-3852/com.example.timothy.binderpoolclient D/BInderPoolClient: onServiceConnected() is called
01-09 08:12:52.717 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: finish to bind service


bindService花费的时间是18ms。

除了绑定的过程,所有的AIDL调用也都是耗时操作,这个和Binder的底层实现有关,所以从性能考虑,AIDL调用都是不适合放在UI线程里面的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息