IPC机制——AIDL——Binder“连接池”

         AIDL的典型使用流程是:首先创建一个Service和一个AIDL接口,接着创建一个类继承自AIDL接口中的Stub类并实现Stub中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客户端就可以绑定服务端Service,建立连接后就可以访问远程服务端的方法了。

        现在考虑一种情况:如果项目规模扩大,现在有10个不同的业务模块都需要使用AIDL来进行进程间通信,那我们该如何处理呢?也许你会说:“按照AIDL的实现方式一个个来吧,”这是可以的,如果用这种方法,首先我们需要创建10个Service,那如果100个业务模块呢?显然这种方式不妥当。

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

         下面对Binder连接池的代码实现做一下说明。首先,提供两个接口(ISecurityCenter和ICompute)来模拟上面提到的多个业务模块都要使用AIDL的情况,其中ISecurityCenter接口提供加密解密功能,声明如下:

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}
         而ICompute接口提供计算加法的功能,声明如下:

interface ICompute {
    int add(int a, int b);
}
接着看两个AIDL接口的实现:

public class SecurityCenterImpl extends ISecurityCenter.Stub {

    private static final char SECRET_CODE = '^';

    @Override
    public String encrypt(String content) throws RemoteException {
        char[] chars = content.toCharArray();
        for(int i = 0; i < chars.length; i++) {
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }
}
public class ComputeImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}
接下来就是服务端和Binder连接池的工作了。首先,为Binder连接池创建AIDL接口IBinderPool.aidl:

interface IBinderPool {
    // @param binderCode, the unique token of specific Binder
    // @return specific Binder who's token is binderCode.
    IBinder queryBinder(int binderCode);
}
接着,为Binder连接池创建远程Service并实现IBinderPool,下面是queryBinder的具体实现,可以看到请求转发的实现方法,当Binder连接池连接上远程服务时,会根据不同模块的标识即bindCode返回不同的Binder对象,通过这个Binder对象所执行的操作全部发生在远程服务端。

 @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
                default:
                    break;
            }
            return binder;
        }
远程Service的代码如下:

public class BinderPoolService extends Service {

    private static final String TAG = "BinderPoolService";

    private Binder mBinderPool = new BinderPool.BinderPoolImpl();

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return mBinderPool;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
下面还剩下Binder连接池的具体实现,在它的内部首先要去绑定远程服务绑定成功后,客户端就可以通过它的queryBinder方法去获取各自对应的Binder,拿到所需的Binder以后,不同业务模块就可以进行各自的操作了,Binder连接池的代码如下:

public class BinderPool {
    private static final String TAG = "BinderPool";

    public static final int BINDER_NONE = -1;
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;

    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private Context mContext;
    private CountDownLatch mConnectBinderPoolCountDownLatch;

    public BinderPool(Context context) {
        mContext = context.getApplicationContext();
        connectBinderPoolService();
    }

    public static BinderPool getInstance(Context context) {
        if(sInstance == null) {
            synchronized (BinderPool.class) {
                if(sInstance == null) {
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    private synchronized void connectBinderPoolService() {
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
        try {
            mConnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * query binder by binderCode from binder pool
     * @param binderCode the unique token of binder
     * @return binder who's token is binderCode
     *          return null when not found or BinderPoolService died.
     */
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        if(mBinderPool != null) {
            try {
                binder = mBinderPool.queryBinder(binderCode);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return binder;
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.w(TAG, "binder died.");
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };

    public static class BinderPoolImpl extends IBinderPool.Stub {
        public BinderPoolImpl() {
            super();
        }

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
                default:
                    break;
            }
            return binder;
        }
    }
}
Binder连接池的具体实现就分析完了,它的好处是显而易见的,针对上面的例子,我们只需要创建一个Service即可完成多个AIDL接口的工作,下面验证效果。新建一个Activity,代码如下:

public class BinderPoolActivity extends AppCompatActivity {
    private static final String TAG = "BinderPoolActivity";

    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                doWork();
            }
        }).start();
    }
    private void doWork() {
        BinderPool binderPool = BinderPool.getInstance(BinderPoolActivity.this);
        IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        mSecurityCenter = SecurityCenterImpl.asInterface(securityBinder);
        Log.d(TAG, "visit ISecurityCenter");
        String msg = "hello Android";
        System.out.println("content: " + msg);
        try {
            String password = mSecurityCenter.encrypt(msg);
            System.out.println("encrypt: " + password);
            System.out.println("decrypt: " + mSecurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "visit ICompute");
        IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        mCompute = ComputeImpl.asInterface(computeBinder);
        try {
            System.out.println("3 + 5 = " + mCompute.add(3, 5));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}
在上述代码中,我们夏侯调用了ISecurityCenter和ICompute这两个AIDL接口中的方法,看一下log,显然,工作正常。



        这里需要补充一下,doWork()为什么要在单独线程中执行呢?这是因为在Binder连接池的实现中,我们通过CountDownLatch将bindService这一异步操作换成了同步操作,意味着它有可能是耗时的,Binder方法的调用过程也可能是耗时的,因此不建议放在主线程执行。注意到BinderPool是一个单例实现,因此在同一个进程中智慧初始化一次,所以如果我们提前初始化BinderPool,那么可以优化程序体验,比如可以放在Application中提前对其进行初始化。另外,BinderPool中有断线重连机制,当远程服务意外终止时,BinderPool会重新建立连接,这个时候如果业务模块中的Binder调用出现了异常,也需要手动去重新获取最新的Binder对象。

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








  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值