博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Binder的使用
阅读量:5890 次
发布时间:2019-06-19

本文共 12684 字,大约阅读时间需要 42 分钟。

 Binder是一个深入的话题,由于Binder太过于复杂,所以本文不涉及底层细节,要想要知道底层细节可以去阅读、这两篇文章。

1、AIDL文件的创建及解析

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

// IBinderPool.aidlpackage 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 statementspublic 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);            }            //返回系统封装后的Stub.proxy对象(代表服务端客户端不在同一进程)            return new com.example.binder.aidl.IBinderPool.Stub.Proxy(obj);        }        //返回当前的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
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
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();复制代码

转载于:https://juejin.im/post/5c789d5de51d454602435f2a

你可能感兴趣的文章
4、Bash基础及配置、标准I/O、管道及shell编程基础;
查看>>
综合技术 --mybatis-config.xml文件
查看>>
Java的缓冲流内部实现及其效率探讨
查看>>
mysql+mycat实现读写分离
查看>>
http协议特点,web资源,url通用格式
查看>>
学习安卓开发[1] - 程序结构、Activity生命周期及页面通信
查看>>
安装和卸载软件包
查看>>
socket_ftp下载实例_完善功能
查看>>
部署 k8s Cluster(下)- 每天5分钟玩转 Docker 容器技术(119)
查看>>
40条常见的移动端Web页面问题解决方案
查看>>
Protostar format4
查看>>
import android.support.v7.app.ActionBarActivity; 报
查看>>
mysql Communications link failure
查看>>
grep进阶与sed行编辑器
查看>>
[置顶] JSTL与Struts的结合(四)
查看>>
读书笔记“使用AngularJs开发下一代web应用”
查看>>
正则表达式
查看>>
思科端口安全总结
查看>>
我的友情链接
查看>>
异常Customer_$$_javassist_2 cannot be cast to javassist.util.proxy.Proxy
查看>>