Android学习之旅-定义自己的Binder连接池[艺术探索学
如果在你的项目中有多个模块需要用到 AIDL 进行进程间通信,按照常规的做法是创建 AIDl 、创建远程 Service 绑定服务然后调用相关方法,原则上是可以完成我们的需求,但是按这种做法带来的后果就是创建了大量的远程 Service 来进行进程间通信,Service 是四大组件之一,是一种系统资源,建了大量 Service 本身就占据来大量的资源让我们的应用看起来很笨重,而且在 Android 手机设置模块的更多应用里是可以看到正在运行的 Service 的,试想一下如果你创建了 20 个 Service 并且处于运行状态,那么用户看到了又是什么感受呢? 针对这种情况我们需要建立一个 BinderPool 来统一协调管理所有的Binder,服务端只负责管理 BinderPool,根据当前传入的标记来返回对应的Binder对象,通过 BinderPool 来将所有的Binder请求统一转发到一个远程Service中去执行,避免创建大量的 Service。
假设我有这么一个需求,在一个APP中有两个模块,分别是模块A和模块B,在模块A中与服务端通信并实现在服务端中添加和获取数据的操作,模块B设计到加密解密的操作与模块A一样我把这些操作统统放到了服务端中操作,客户端只需要调用即可,为此我定义了两个 AIDL 文件。
// IInFoManager.aidl 模块A
package com.example.tengfei.client;
import com.example.tengfei.client.Info;
interface IInFoManager {
void addInformation(in Info info);
List<Info> getInformationList();
}
// IPassWordManager.aidl 模块B
package com.example.tengfei.client;
interface IPassWordManager {
void encryption(String password);
String decrypt();
}
但是有一个问题,如果跨进程通信那么每一个 AIDL 文件就对应着一个 服务端的 Service,但是创建大量的Service不仅占据了大量的资源,而且还不适合管理,那么有什么办法可以避免呢?针对这种情况,我定义了一个 IBinderPool.aidl 让我们的服务端 Service 只与 IBinderPool.aidl 文件生成的 Binder 对象关联。
// IBinderPool.aidl 定义 aidl 文件,用于在客户端调用 queryBinder 方法时通过传入的 code 来返回客户端所需要的 Binder 对象
package com.example.tengfei.client;
interface IBinderPool {
IBinder queryBinder(int code);
}
在此文件中定义了 queryBinder 方法,用于根据传入的 code 来返回客户端所需要的 Binder 对象,为了统一管理所有的 Binder 对象,需要定义一个 BinderPool 来协调管理所有的 Binder 对象,所以说在每一次客户端绑定远程服务的时候其实就是在BinderPool中通过 bindService 绑定服务,在 onServiceConnected 中获得 IBinderPool.aidl 文件的接口类型的对象,并通过 IBinderPool 来调用服务端的 queryBinder 方法,通过传入的 code 来判断最终返回的 Binder 对象。
为了使我们自己定义的相关功能的 AIDL 文件的 Binder 对象与服务端的 Service 解耦,需要我们自己去定义一个类去继承相关 AIDL 文件的 binder 扩展类。
public class InfoManagerImpl extends IInFoManager.Stub {
private CopyOnWriteArrayList copyOnWriteArrayList;
public InfoManagerImpl(CopyOnWriteArrayList copyOnWriteArrayList) {
this.copyOnWriteArrayList = copyOnWriteArrayList;
}
@Override
public void addInformation(Info info) throws RemoteException {
copyOnWriteArrayList.add(info);
}
@Override
public List<Info> getInformationList() throws RemoteException {
return copyOnWriteArrayList;
}
}
/**
* @author tengfei 简化处理,加解密的操作可以自己实现
*/
public class PassWordManagerImpl extends IPassWordManager.Stub {
@Override
public String encryption(String password) throws RemoteException {
return password;
}
@Override
public String decrypt(String password) throws RemoteException {
return password+"#####";
}
}
定义BinderPool
public class BinderPool {
public static final int PASSWORD_MANAGER_IMPL_CODE = 0x001;
public static final int INFO_MANAGER_IMPL_CODE = 0x002;
private static volatile BinderPool sInstance;
private Context context;
private IBinderPool iBinderPool;
private CountDownLatch mConnectBinderPoolDownLatch;
private BinderPool(Context context) {
this.context = context;
connectionBinderPoolService();
}
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
iBinderPool.asBinder().unlinkToDeath(deathRecipient, 0);
iBinderPool = null;
connectionBinderPoolService();
}
};
private ServiceConnection binderPoolServiceConnnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iBinderPool = IBinderPool.Stub.asInterface(service);
try {
iBinderPool.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void connectionBinderPoolService() {
mConnectBinderPoolDownLatch = new CountDownLatch(1);
Intent intent = new Intent(context, BinderPoolService.class);
context.bindService(intent, binderPoolServiceConnnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static BinderPool getInstance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
public synchronized IBinder queryBinder(int code) {
IBinder binder = null;
if (iBinderPool != null) {
try {
binder = iBinderPool.queryBinder(code);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return binder;
}
}
BinderPool是一个单例类,因此在一个进程中只会初始化一次,在它的构造方法中执行绑定 Service 的操作,并且为它设置了死亡代理,在 ServiceConnection 中 onServiceConnected 方法中初始化 IBinderPool 的接口类型的对象,定义 queryBinder 方法,在其内部通过 IBinderPool 的 queryBinder 来根据 code 参数来获取服务端返回给客户端的 Binder 对象。
定义远程 Serivce BinderPoolService.java
public class BinderPoolService extends Service {
private CopyOnWriteArrayList<Info> infoCopyOnWriteArrayList = new CopyOnWriteArrayList<>();
private IBinder iBinder = new IBinderPool.Stub() {
@Override
public IBinder queryBinder(int code) throws RemoteException {
switch (code) {
case PASSWORD_MANAGER_IMPL_CODE:
return new PassWordManagerImpl();
case INFO_MANAGER_IMPL_CODE:
return new InfoManagerImpl(infoCopyOnWriteArrayList);
default:
break;
}
return new PassWordManagerImpl();
}
};
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
}
服务端的代码还是非常简单的,主要就是初始化服务端的 Binder 对象,在 onBind 方法中返回,BinderPool 的 onServiceConnected 中所获取到服务端的 Binder 对象就是此对象。
在客户端中调用
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "bt_addInformation");
BinderPool mBinderPool = BinderPool.getInstance(BinderPoolActivity.this);
IBinder binder = mBinderPool.queryBinder(BinderPool.INFO_MANAGER_IMPL_CODE);
//将服务端返回的 Binder 对象转换为客户端需要的 AIDL 接口类型的对象
IInFoManager inFoManager = IInFoManager.Stub.asInterface(binder);
try {
inFoManager.addInformation(new Info(0x001, "#info 01"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
首先获取到 BinderPool 对象,根据 BinderPool 来获取到相对应的服务端的Binder对象,将服务端的 Binder 对象转换为客户端所需要的 AIDL 接口类型的对象并调用相关的方法,在客户端调用之所以放在子线程中是因为通过 CountDownLatch 将 bindService 这一个异步操作转换为了同步操作,同时通过Binder调用远程服务端的方法也可能是耗时的,所以建议在子线程中执行相关的代码。
参考资料
1.Android开发艺术探索