Binder连接池

概述

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

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

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线程里面的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值