Binder线程池

2,172 阅读4分钟

AIDL是最常用的进程间通信方式,这里简单回顾一下流程:首先创建一个Service和一个AIDL接口,接着创建一个类继承自AIDL接口中的Stub类并实现Stub中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客户端就可以绑定服务端Service,建立连接后就可以访问远程服务端的方法了。

公司的项目越来越庞大了,现在有10个不同的业务模块都需要使用AIDL来进行进程间通信,那我们该怎么处理呢?也许你会说:“就按照AIDL的实现方式一个个来吧”,这是可以的,如果用这种方法,首先我们需要创建10个Service,这好像有点多啊!如果有100个地方需要用到AIDL呢,先创建100个Service?显然是不可能的,我们需要减少Service的数量,将所有的AIDL放在同一个Service中去管理。

在这种模式下,整个工作机制是这样的:每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间是不能有耦合的,所有实现细节我们要单独开来,然后向服务端提供自己的唯一标识和其对应的Binder对象;对于服务端来说,只需要一个Service就可以了,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了。由此可见,Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程 。

这种模式的实现思路大概是这样的:创建IBinderPool.aidl接口,里面提供一个query方法,根据不同的识别码返回不同的Binder对象,然后创建类BinderPoolImpl实现IBinderPool.Stub,真正去实现query方法。创建唯一的Service类,onBind方法里面返回BinderPoolImpl的实例。

interface IBinderPool {
    IBinder query(int binderCode);
}

public static class BinderPoolImpl extends IBinderPool.Stub {

        @Override
        public IBinder query(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
                case BINDER_SECURITY:
                    binder = new ScurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
            }
            return binder;
        }
    }
    

最后是BinderPool的实现,为单例模式,在构造方法里面连接远程服务,连接上了即可得到IBinderPool对象,就可以调用远程的query方法,然后对外提供一个queryBinder方法根据标识码来返回Binder对象的方法即可。

private BinderPool(Context context) {
        this.context = context.getApplicationContext();
        //连接远程服务
        connectBinderPoolService();
    }

    public static BinderPool getInstance(Context context) {
        if (binderPool == null) {
            synchronized (BinderPool.class) {
                if (binderPool == null) {
                    binderPool = new BinderPool(context);
                }
            }
        }
        return binderPool;
    }
    
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        if (iBinderPool != null) {
            try {
                binder = iBinderPool.query(binderCode);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return binder;
    }
    
     private void connectBinderPoolService() {
        countDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(context, BinderPoolService.class);
        context.bindService(intent, connection, Context.BIND_AUTO_CREATE);

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private ServiceConnection connection = 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();
            }
            countDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

最后就是在Activity中的调用了:

new Thread(new WorkRun()).start();

 private class WorkRun implements Runnable {

        @Override
        public void run() {
            doWork();
        }
    }
    
private void doWork() {
        BinderPool binderPool = BinderPool.getInstance(this);
        IBinder binder = binderPool.queryBinder(BinderPool.BINDER_SECURITY);
        ISecurityCenter securityCenter = ScurityCenterImpl.asInterface(binder);
        try {
            String str = securityCenter.encrypt("helloworld-安卓");
            Log.i(TAG, "s: " + str);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        IBinder binder1 = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        ICompute compute = ComputeImpl.asInterface(binder1);
        try {
            int sum = compute.add(2, 6);
            Log.i(TAG, "sum: " + sum);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

需要完整Demo的可以参考这个例子

这里需要额外说明一下,为什么要在线程中去执行呢?这是因为在Binder连接池的实现中,我们通过CountDownLatch将bindService这一异步操作转换成了同步操作,这就意味着它有可能是耗时的,然后就是Binder方法的调用过程也可能是耗时的,因此不建议放在主线程去执行。注意到BinderPool是一个单例实现,因此在同一个进程中只会初始化一次,所以如果我们提前初始化BinderPool,那么可以优化程序的体验,比如我们可以放在Application中提前对BinderPool进行初始化,虽然这不能保证当我们调用BinderPool时它一定是初始化好的,但是在大多数情况下,这种初始化工作(绑定远程服务)的时间开销(如果BinderPool没有提前初始化完成的话)是可以接受的。另外,BinderPool中有断线重连的机制,当远程服务意外终止时,BinderPool会重新建立连接,这个时候如果业务模块中的Binder调用出现了异常,也需要手动去重新获取最新的Binder对象,这个是需要注意的。

有了BinderPool可以大大方便日常的开发工作,比如如果有一个新的业务模块需要添加新的AIDL,那么在它实现了自己的AIDL接口后,只需要修改BinderPoolImpl中的queryBinder方法,给自己添加一个新的binderCode并返回对应的Binder对象即可,不需要做其他修改,也不需要创建新的Service。由此可见,BinderPool能够极大地提高AIDL的开发效率,并且可以避免大量的Service创建,因此,建议在AIDL开发工作中引入BinderPool机制。