内容简介:Binder是一个深入的话题,由于Binder太过于复杂,所以本文不涉及底层细节,要想要知道底层细节可以去阅读Binder使用起来还是比较简单的,创建一个IBinderPool.aidl文件然后生成的Java文件如下:
Binder是一个深入的话题,由于Binder太过于复杂,所以本文不涉及底层细节,要想要知道底层细节可以去阅读 Android Bander设计与实现 - 设计篇 、 写给 Android 应用工程师的 Binder 原理剖析 这两篇文章。
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); } //返回系统封装后的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<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(); 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
指数型组织
萨利姆•伊斯梅尔 (Salim Ismail)、迈克尔•马隆 (Michael S. Malone)、尤里•范吉斯特 (Yuri van Geest) / 苏健 / 浙江人民出版社 / 2015-8-1 / CNY 69.90
《指数型组织》是一本指数级时代企业行动手册。作者奇点大学创始执行理事萨利姆·伊斯梅尔归纳了指数型组织的11个强大属性,并提出了建立指数型组织的12个关键步骤。通过自己创建的一套“指数商”测试题,伊斯梅尔还测量出了指数型组织世界100强。 为什么小米、海尔和阿里巴巴能进入“指数型组织世界100强”名单?“独角兽”Uber、Airbnb、谷歌等知名企业是如何指数化自己的组织的? 未......一起来看看 《指数型组织》 这本书的介绍吧!