Binder连接池

来源:互联网 发布:mac 无法开机无法充电 编辑:程序博客网 时间:2024/05/21 20:28

概述

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

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

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线程里面的。
0 0
原创粉丝点击