天天看点

Android 进程间通信之 AIDL (二)高级用法及建议

  1. Bindler 连接池

          这里引用了任玉刚老师的<Android 开发艺术探索> 中的 Binder 连接池概念,本来想自己总结的,想了下其实任玉刚老师总结的挺好的就照搬过来了,一个字“懒”啊!

Android 进程间通信之 AIDL (二)高级用法及建议

。上一篇咱们介绍到了Android AIDL 基本用法还没有看过的同学可以先去温习下。

           现在假设有 N 个不同的业务模块需要使用 AIDL 来进行进程间通信,那我们该怎么处理勒?或许你会说“就按照  AIDL 的实现方式一个一个来实现吧”你有没有想过这样会创建 N 个 Service,当用户打开手机设置查看正在运行的进程,发现某某某 APP Service TMD 有10 个 甚至更多。他的第一反应也许是该卸载了,我们不可能无限制的创建 Service ,Service 是四大组件之一,是需要消耗内存的。而且太多的话会使我们的 APP 看起来特别很重量级。为了避免这种问题发生,咱们可以引用 Binder 连接池来解决这个问题,下面对 Bindler 连接池的代码实现做一下说明。首先,为了说明问题,我们提供了两个 AIDL 接口 ISecurityCenter 和 Icompute 来模拟上面提到的多模块应用的情况。

        一. 创建 ISecurityCenter ,Icompute 接口。

package android.t01.com.common;

// Declare any non-default types here with import statements

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}           
package android.t01.com.common;

// Declare any non-default types here with import statements

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 连接池的准备工作。

             不知道有没有同学发现,咱们并没有为每个 Moudle AIDL 去单独创建 Service。

              1. 首先,创建 连接池接口 IBindlerPool.aidl。

interface IBinderPool {
    /**
     * @param binderCode, the unique token of specific Binder<br/>
     * @return specific Binder who's token is binderCode.
     */
    IBinder queryBinder(int binderCode);
}           

                2. 实现 连接池接口

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;
        }
    }           

                3. 创建服务端的 Servicer.

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();
    }

}           

                     看起来是不是特简单

Android 进程间通信之 AIDL (二)高级用法及建议

                4. Bindler 连接池的具体实现。

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 Context mContext;
    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private CountDownLatch mConnectBinderPoolCountDownLatch;

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

    public static BinderPool getInsance(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<br>
     *         return null when not found or BinderPoolService died.
     */
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        try {
            if (mBinderPool != null) {
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // ignored.
        }

        @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();
        }
    };


    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;
        }
    }
}           
  • Bindler  连接池的具体实现分析就完了,它的好处是显然易见的。

                5. 客服端操作。

private void doWork() {
        BinderPool binderPool = BinderPool.getInsance(MyBookActivity.this);
        IBinder securityBinder = binderPool
                .queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        ;
        mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
                .asInterface(securityBinder);
        Log.d(TAG, "visit ISecurityCenter");
        String msg = "helloworld-安卓";
        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);
        ;
        ICompute mCompute = ComputeImpl.asInterface(computeBinder);
        try {
            System.out.println("3+5=" + mCompute.add(3, 5));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }           

        注意:这里需要额外的说明下,为什么需要在子线程中去执行了?这是因为在 Bindler 连接池的实现中,我们需要通过 CountDownLatch 将 bindService 这一异步操作转换为同步操作,这就意味着他是需要耗时的并且 Bindler 中的方法调用也是耗时的,因此不建议放在主线程。

    总结

        有了 BinderPool 可以大大方便和提高日常的开发工作,比如如果有一个新的业务模块 B 需要添加新的 AIDL,那么在它实现了自己的 AIDL 接口后,只需要修改 BinderPoolImpl 中的 queryBinder 方法,给自己新添加一个 binderCode 并返回对应的 Binder 对象即可!

    建议

名称 优点 缺点 适用场景
 Bundle 简单易用 只能传输 Bundle 支持的数据类型    四大组件的进程间
文件共享 简单易用 不适合高并发场景,并且无法做到进程间的即时通信 无并发访问情形,交换简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发,支持实时通信 使用稍复杂,需要处理好线程同步 一对多通信且有 RPC 需求
Messenger 功能一般,支持一对多串行通信,支持实时通信 不能很好处理高并发,只能传输 Bundle 数据 低并发一对多
ContentProvider 在数据源访问方面功能强大,支持一对多并发数据共享,可通过 Call 方法扩展其它操作 可以理解为受约束的 AIDL 一对多的进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现细节稍微有点烦琐,不支持直接的 RPC 网络数据交换

Demo 地址

继续阅读