Binder连接池
来源:互联网 发布:mac 无法开机无法充电 编辑:程序博客网 时间:2024/05/21 20:28
概述
有下面这样一种应用场景:一个Android应用有10个功能模块,供主应用通过aidl调用。按照aidl的标准使用方法,就是创建10个aidl接口并写10个类实现他们,然后创建10个服务,在这10个服务的onBind里面返回接口的实现。那么问题来了,有必要为每个模块都创建一个服务吗?如果有100个模块呢?也要这么创建100个服务吗?
答案是否定的,解决这样一个场景的方法就是Binder连接池。
Binder连接池的结构如下图所示:
BindPoolService是一个通过标准方式建立的远程服务,它只有一个方法:query。这个方法根据传递进去的参数返回对应的IBinder,客户端就可以利用这个IBinder调用不同模块的方法。在这种模式下,系统中只有一个服务在后台运行。
下面就来实现这样一种解决方案。
实现服务端
新建两个模块的aidl接口,并实现之。
// IAddCenter.aidlpackage com.example.timothy.binderpoolservice;interface IAddCenter { int add(int a, int b);}// ISubCenter.aidlpackage com.example.timothy.binderpoolservice;interface ISubCenter { int sub(int a, int b);}// AddCenterImpl.javapublic class AddCenterImpl extends IAddCenter.Stub { @Override public int add(int a, int b) throws RemoteException { return a + b; }}// SubCenterImpl.javapublic class SubCenterImpl extends ISubCenter.Stub { @Override public int sub(int a, int b) throws RemoteException { return a - b; }}
定义IBinderPool接口并实现之。
// IBinderPool.aidlpackage com.example.timothy.binderpoolservice;interface IBinderPool { IBinder query(int code);}// BinderPoolImpl.javapublic 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.javapublic 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 called01-09 08:12:52.699 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: start to bind service01-09 08:12:52.715 3852-3852/com.example.timothy.binderpoolclient D/BInderPoolClient: onServiceConnected() is called01-09 08:12:52.717 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: finish to bind service01-09 08:12:52.717 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: query() is called, code = 001-09 08:12:52.721 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: query() is called, code = 101-09 08:12:52.727 3852-3882/com.example.timothy.binderpoolclient D/BinderPoolClient: add ret is :1301-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 service01-09 08:12:52.715 3852-3852/com.example.timothy.binderpoolclient D/BInderPoolClient: onServiceConnected() is called01-09 08:12:52.717 3852-3882/com.example.timothy.binderpoolclient D/BInderPoolClient: finish to bind service
bindService花费的时间是18ms。
- 除了绑定的过程,所有的AIDL调用也都是耗时操作,这个和Binder的底层实现有关,所以从性能考虑,AIDL调用都是不适合放在UI线程里面的。
- IPC-Binder连接池
- Binder连接池
- Binder连接池
- binder连接池
- Binder连接池
- Android Binder连接池
- Binder连接池
- IPC小记Binder连接池
- IPC机制之五:Binder连接池
- AIDL使用及Binder连接池
- Binder连接池的实现步骤
- 实现AIDL接口的Binder连接池
- 关于BinderPool Binder连接池的愚见
- IPC机制之Binder连接池
- AIDL学习(三)---Binder连接池
- aidl通信进阶,使用binder连接池
- Binder连接池连接多个AIDL文件的处理
- [看书日记20160113]Binder连接池,CrashHandler,multidex
- PAT乙级1027
- UIWebView保存图片
- Spring面试,IoC和AOP的理解
- 2016年第七届蓝桥杯C/C++程序设计_编程题9题_交换瓶子
- 十五个常用Linux命令行总汇
- Binder连接池
- 《JAVA与模式》之观察者模式
- Linux 之 图形界面与命令行界面间的转换
- Postgresql 当中有四种方式获取当前时间
- 当代移动通信发展四个阶段
- SQL行转列
- Paros数据抓包
- Oracle学习记录二——SQL基础
- 鼠标和键盘监听pro(消息弹出框)