Android Binder的使用

作者: Android开发技术分享 | 来源:发表于2019-03-02 21:29 被阅读5次

    1、AIDL文件的创建及解析
    Binder使用起来还是比较简单的,创建一个IBinderPool.aidl文件然后clean一下,就可以给我们生成一个Java文件。

    // IBinderPool.aidl
    package com.example.binder.aidl;
    interface IBinderPool {
        IBinder queryBinder(int binderCode);
    }
    

    生成的Java文件如下:

    /*
     * This file is auto-generated.  DO NOT MODIFY.
     * Original file: D:\\AndroidDemo\\BinderDemo\\app\\src\\main\\aidl\\com\\example\\binder\\aidl\\IBinderPool.aidl
     */
    package com.example.binder.aidl;
    // Declare any non-default types here with import statements
    
    public interface IBinderPool extends android.os.IInterface {
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.example.binder.aidl.IBinderPool {
            //Binder的唯一标识符,一般用当前Binder的类名表示,比如这里的com.example.binder.aidl.IBinderPool
            private static final java.lang.String DESCRIPTOR = "com.example.binder.aidl.IBinderPool";
    
            /**
             * Construct the stub at attach it to the interface.
             * 
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.example.binder.aidl.IBinderPool interface,
             * generating a proxy if needed.
             * 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型对象,这种类型转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象
             */
            public static com.example.binder.aidl.IBinderPool asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                //查询本地接口
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                //如果存在直接返回该对象(代表服务端与客户端在同一进程)
                if (((iin != null) && (iin instanceof com.example.binder.aidl.IBinderPool))) {
                    return ((com.example.binder.aidl.IBinderPool) iin);
                }
    //返回当前的Binder对象
            @Override
            public android.os.IBinder asBinder() {
                return this;
            }
            //该方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理
            @Override
            public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
                switch (code) {
                    case INTERFACE_TRANSACTION: {
                        reply.writeString(DESCRIPTOR);
                        return true;
                    }
                    case TRANSACTION_queryBinder: {
                        data.enforceInterface(DESCRIPTOR);
                        int _arg0;
                        _arg0 = data.readInt();
                        android.os.IBinder _result = this.queryBinder(_arg0);
                        reply.writeNoException();
                        reply.writeStrongBinder(_result);
                        //返回false则代表客户端请求失败,可以根据特性来做权限验证
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    
            private static class Proxy implements com.example.binder.aidl.IBinderPool {
                private android.os.IBinder mRemote;
    
                Proxy(android.os.IBinder remote) {
                    mRemote = remote;
                }
    
                @Override
                public android.os.IBinder asBinder() {
                    return mRemote;
                }
    
                public java.lang.String getInterfaceDescriptor() {
                    return DESCRIPTOR;
                }
                /**
                 * 此方法运行在服务端。
                 */
                @Override
                public android.os.IBinder queryBinder(int binderCode) throws android.os.RemoteException {
                    //创建输入类型Parcel对象
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    //创建输出类型Parcel对象
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    //创建返回值对象
                    android.os.IBinder _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        //写入请求参数
                        _data.writeInt(binderCode);
                        //发起RPC(远程过程调用请求),同时当前线程挂起;然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行
                        mRemote.transact(Stub.TRANSACTION_queryBinder, _data, _reply, 0);
                        _reply.readException();
                        //从_reply中取出返回数据
                        _result = _reply.readStrongBinder();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
            }
    
            static final int TRANSACTION_queryBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        }
    
        public android.os.IBinder queryBinder(int binderCode) throws android.os.RemoteException;
    }
    

    注意:首先当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所有如果一个远程方法是很耗时的,那么就不能在UI线程中发起此远程请求;其次,由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采取同步的方式去实现,因为它已经运行在一个线程中了
    2、Binder的使用
     上面实现AIDL文件的创建,那么如何使用尼?其实也比较简单,创建一个Service,在其onBind里返回一个服务端Binder对象,在客户端的ServiceConnection里拿到这个Binder对象。

    //服务端
    public class BinderPoolService extends Service {
        private static final String TAG = "BinderPoolService";
    
        private Binder mBinderPool = new BinderPool.BinderPoolImpl();
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return mBinderPool;
        }
    }
    //客户端
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //拿到服务端返回的Binder接口
                mBinderPool = IBinderPool.Stub.asInterface(service);
                ...
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
    Intent intent = new Intent(mContext, BinderPoolService.class);
    mContext.bindService(intent, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
    

    3、Binder池的实现
     在《Android艺术探索》这本书中有一个Binder池概念,意思也就跟线程池、数据库连接池概念一样。避免为每个Binder都创建一个Service,在一个service里根据不同业务拿到其对应的Binder对象。这样就能节省很多资源,毕竟Service也是系统资源。具体实现如下:

    //
    public class BinderPoolService extends Service {
    
    
        private static final String TAG = "BinderPoolService";
    
        private Binder mBinderPool = new BinderPool.BinderPoolImpl();
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return mBinderPool;
        }
    }
    //
    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 static volatile BinderPool mInstance;
        private CountDownLatch mConnectBinderPoolCountDownLatch;
        private IBinderPool mBinderPool;
    
        public BinderPool(Context context) {
            mContext = context.getApplicationContext();
            connectBinderPoolService();
        }
    
        public static BinderPool getInstance(Context context) {
            if (mInstance == null) {
                synchronized (BinderPool.class) {
                    if (mInstance == null) {
                        mInstance = new BinderPool(context);
                    }
                }
            }
            return mInstance;
        }
    
        private synchronized void connectBinderPoolService() {
            mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
            //开启一个服务
            Intent intent = new Intent(mContext, BinderPoolService.class);
            mContext.bindService(intent, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
            try {
                //阻塞,不再往下继续执行
                mConnectBinderPoolCountDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        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) {
                //拿到服务端返回的Binder对象,
                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.i(TAG, "binder die");
                //当断开连接时回调的方法
                mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
                mBinderPool = null;
                //重新连接
                connectBinderPoolService();
            }
        };
    
        public static class BinderPoolImpl extends IBinderPool.Stub {
    
            @Override
            public IBinder queryBinder(int binderCode) throws RemoteException {
                IBinder binder = null;
                //根据code返回不用的Binder
                switch (binderCode) {
                    case BINDER_SECURITY_CENTER:
                        binder = new SecurityCenterImpl();
                        break;
                    case BINDER_COMPUTE:
                        binder = new ComputeImpl();
                        break;
                }
                return binder;
            }
        }
    
    }
    

    4、延伸
     延伸一:Binder是可能意外死亡的,这往往是由于服务端进程意外停止了,这时候就需要重新连接服务。那么如何监听服务端是否死亡尼?有如下两种方法:

    给Binder设置DeathRecipient监听,当Binder死亡后,会收到binderDied方法的回调,在binderDied里可以实现重连远程服务。

     private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
            @Override
            public void binderDied() {
                Log.i(TAG, "binder die");
                //当断开连接时回调的方法
                mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
                mBinderPool = null;
                //重新连接
                connectBinderPoolService();
            }
        };
    //设置监听
                try {
                   mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
    

    在onServiceDisConnected中重连服务

        private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
               ...
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
            //进行服务重连
            }
        };
    

    以上两种方法可以任意选用,但是onServiceDisconnected是在UI线程中调用的,binderDied是在客户端的Binder线程池中调用的。
     延伸二:在平常开发中会经常使用观察者这个设计模式,那么在多进程之间如何实现这种设计模式尼?很简单,在服务端用一个集合来管理对象,然后来进行注册与反注册即可。代码如下:

    //CopyOnWriteArrayList是线程同步的
    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
     private Binder mBinder = new IBookManager.Stub() {
            ...
            @Override
            public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
                mListenerList.add(listener);
            }
    
            @Override
            public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
                mListenerList.remove(listener);
            }
        };
    

    上面代码无法实现观察者
     但是上面的实现对吗?当测试的时候就会发现反注册并没有什么用。也就是上面的实现是错误的,为什么尼?因为Binder会把客户端传递过来的对象重新转化并生成一个新的对象,虽然注册与反注册过程中使用的是同一个客户端对象,但是通过Binder传递到服务端后,却会产生两个全新的对象。由于对象是不能跨进程传输的,所以对象的跨进程传输本质上都是反序列化过程,这也就是AIDL中的自定义对象都必须要实现parcelable的原因。
     那么该如何跨进程实现观察者尼?可以用RemoteCallbackList这个集合。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口,工作原理很简单,在它的内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型。当客户端终止后,它能自动移除与该客户端有关的对象,内部自动实现了线程同步的功能。那么就将上面的代码中的CopyOnWriteArrayList换成RemoteCallbackList,代码如下:

      private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
    
        private Binder mBinder = new IBookManager.Stub() {
            ...
            @Override
            public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
                mListenerList.register(listener);
            }
    
            @Override
            public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
                mListenerList.unregister(listener);
                int N = mListenerList.beginBroadcast();
                mListenerList.finishBroadcast();
            }
        };
    

    这样就实现了跨进程版的观察者。在使用RemoteCallbackList时需要注意一点,无法像操作List一样去操作它,尽管它的名字中带有List,但它并不是一个List。遍历RemoteCallbackList必须按照下面的方式进行,其中beginBroadcast与finishBroadcast必须配对使用,那么仅仅只是获取RemoteCallbackList中的元素个数。

                int N = mListenerList.beginBroadcast();
                Log.i(TAG, "unregisterListener current size: " + N);
                mListenerList.finishBroadcast();
    

    相关文章

      网友评论

        本文标题:Android Binder的使用

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