安卓开发 Binder连接池

作者: 贼厉害 | 来源:发表于2018-05-16 20:40 被阅读109次

    综述

        安卓IPC(进程间通信)可以利用AIDL(Android Interface definition language)来实现,通过Service返回一个IBinder来实现进程间通信。但是随着我们项目需求的增加,我们总不能每个模块都开启一个service吧?那么有没有一种方案可以让一个service同时管理我们所有的IPC业务呢?
        答案当然是肯定的,《Android开发艺术探索》给出了一个Binder连接池的概念,我们都知道利用AIDL进行进程间通信原理其实是Service端为我们创建了一个Binder,并把这个Binder的实例返回给客户端,我们就可以用这个Binder跟service层通信了。我们只需要让service返回不同的Binder就可以实现连接池的功能。

    实现

    我们先定义一个IBinderPool实现连接池的AIDL:

    // IBinderPool.aidl
    package com.liujiakuo.learnmvp;
    
    interface IBinderPool {
    
        IBinder getBinder(int binderCode);
    
    }
    

    这个AIDL定义了一个接口方法,它通过传入的BinderCode来获取一个IBinder。
    接下来我们定义两个不同的AIDL来实现不同的功能,定义一个ISum.aidl来实现求和的功能,一个IMax.aidl实现求最大值的功能。

    // ISum.aidl
    package com.liujiakuo.learnmvp;
    
    interface ISum {
    
        int sum(int a,int b);
    }
    
    // IMax.aidl
    package com.liujiakuo.learnmvp;
    
    interface IMax {
    
        int max(in int[] values);
    }
    

    我们实现上面两个接口:

    public class SumImpl extends ISum.Stub {
    
        @Override
        public int sum(int a, int b) throws RemoteException {
            return a+b;
        }
    }
    
    public class MaxImpl extends IMax.Stub {
        @Override
        public int max(int[] values) throws RemoteException {
            int max = Integer.MIN_VALUE;
            for (int v :values) {
                if(v>max)
                    max=v;
            }
            return max;
        }
    }
    

    Service的写法:BinderPoolService.java

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.util.Log;
    
    public class BinderPoolService extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new BinderPoolImpl();
        }
    
        class BinderPoolImpl extends IBinderPool.Stub {
            @Override
            public IBinder getBinder(int binderCode) {
                IBinder binder = null;
                switch (binderCode) {
                    case BinderPoolUtils.BINDER_MAX:
                        Log.d("TAG", "getBinder: "+BinderPoolUtils.BINDER_MAX);
                        binder = new MaxImpl();
                        break;
                    case BinderPoolUtils.BINDER_SUM:
                        Log.d("TAG", "getBinder: "+BinderPoolUtils.BINDER_SUM);
                        binder = new SumImpl();
                        break;
                }
                return binder;
            }
        }
    }
    
    

    我们来写一个工具类来为我们绑定Service并拿到我们想要的Binder。

    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.util.Log;
    
    import java.util.concurrent.CountDownLatch;
    
    public class BinderPoolUtils {
        public static final int BINDER_SUM = 1;
        public static final int BINDER_MAX = 2;
    
        private static BinderPoolUtils poolUtils;
        private Context context;
        private CountDownLatch countDownLatch;
        private IBinderPool mBinderPool;
    
    
        private BinderPoolUtils(Context context) {
            this.context = context;
            bindServicePool();
        }
    
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mBinderPool = IBinderPool.Stub.asInterface(service);
                try {
                    mBinderPool.asBinder().linkToDeath(deathRecipient, 0);//监听Binder的存货状态
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {}
        };
        private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
            @Override
            public void binderDied() {//Binder死掉之后重新绑定
                mBinderPool.asBinder().unlinkToDeath(deathRecipient, 0);//重置死亡状态
                mBinderPool = null;
                bindServicePool();//重新连接
            }
        };
    
        private void bindServicePool() {
            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();
            }
        }
    
        public static BinderPoolUtils getInstance(Context context) {
            if (poolUtils == null) {
                synchronized (BinderPoolUtils.class) {
                    if (poolUtils == null) {
                        poolUtils = new BinderPoolUtils(context);
                    }
                }
            }
            return poolUtils;
        }
    
        public IBinder getBinder(int binderCode) {
            IBinder binder = null;
            try {
                binder = mBinderPool.getBinder(binderCode);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            return binder;
        }
    }
    

    代码很简单,就是实现了一个单例模式的工具类,bindServicePool负责绑定service,countDownLatch负责阻塞线程,因为bindService()是异步的,onServiceConnected方法不会马上回调,这样我们如果在回调之前执行getBinder会有空指针问题,所以利用CountDownLatch在绑定时阻塞,在onServiceConnected回调拿到Binder之后释放。但是这里要注意的是在activity里面调用的时候要考虑线程阻塞的问题。

    用法

    我是在MainActivity的onCreate里面这样写的:

    new Thread(new Runnable() {
                @Override
                public void run() {
                    BinderPoolUtils pool = BinderPoolUtils.getInstance(MainActivity.this);
                    //try {
                        //IMax maxBinder = IMax.Stub.asInterface(pool.getBinder(BinderPoolUtils.BINDER_MAX));
                        //ISum sumBinder = ISum.Stub.asInterface(pool.getBinder(BinderPoolUtils.BINDER_SUM));
                        //Log.d("TAG", "max: "+maxBinder.max(new int[]{1,2,3,-1}));
                        //Log.d("TAG", "sum: "+sumBinder.sum(1,7));
                   //} catch (Exception e) {
                        //e.printStackTrace();
                    //}
                }
            }).start();
    

    为了不阻塞UI线程,我开启一个新的线程去执行。注释部分是获取相应的Binder,这部分可能要跟控件的事件绑定,当getInstance执行完之后,IBinderPool实例就拿到了,这时候可以利用handler通知主线程绑定点击事件,要不然会出现上面提到的空指针。

    相关文章

      网友评论

      本文标题:安卓开发 Binder连接池

      本文链接:https://www.haomeiwen.com/subject/nxzpdftx.html