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线程里面的。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件