如果在你的项目中有多个模块需要用到 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 开发艺术探索
来源: http://www.jianshu.com/p/c773cbaea8b7