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 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.IBinderPoolprivate 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对象@Overridepublic android.os.IBinder asBinder() {return this;}//该方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理@Overridepublic 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;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}/*** 此方法运行在服务端。*/@Overridepublic 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@Overridepublic IBinder onBind(Intent intent) {return mBinderPool;}
}
//客户端
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//拿到服务端返回的Binder接口mBinderPool = IBinderPool.Stub.asInterface(service);...}@Overridepublic 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@Overridepublic 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() {@Overridepublic 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();}@Overridepublic void onServiceDisconnected(ComponentName name) {}};private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {Log.i(TAG, "binder die");//当断开连接时回调的方法mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);mBinderPool = null;//重新连接connectBinderPoolService();}};public static class BinderPoolImpl extends IBinderPool.Stub {@Overridepublic IBinder queryBinder(int binderCode) throws RemoteException {IBinder binder = null;//根据code返回不用的Binderswitch (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() {@Overridepublic 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() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {...}@Overridepublic void onServiceDisconnected(ComponentName name) {//进行服务重连}};
复制代码

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

//CopyOnWriteArrayList是线程同步的
private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();private Binder mBinder = new IBookManager.Stub() {...@Overridepublic void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {mListenerList.add(listener);}@Overridepublic 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() {...@Overridepublic void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {mListenerList.register(listener);}@Overridepublic 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的使用相关推荐

  1. Android Binder IPC机制

    之前有很多人问过我TaintDroid的内容,所以一点点吧涉及的思路整理一下. 今天又看了一遍,确实需要很多的技术支持以及扎实的功底,不管是Java的还是Android,从系统底层一直到顶层,涉及的知 ...

  2. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  3. aidl使用_借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉

    AIDL 是 Android Interface Definition Language(Android 接口定义语言)的缩写,它是 Android 进程间通信的接口语言.由于 Android 系统的 ...

  4. Android Binder Driver缺陷导致定屏问题分析

    本文讲解异步Android binder call是如何阻塞整个系统的,通过ramdump信息以及binder通信协议来演绎并还原定屏现场. 一.背景知识点 解决此问题所涉及到的基础知识点有:Trac ...

  5. 理解Android Binder机制(3/3):Java层

    本文是Android Binder机制解析的第三篇,也是最后一篇文章.本文会讲解Binder Framework Java部分的逻辑. Binder机制分析的前面两篇文章,请移步这里: 理解Andro ...

  6. 理解Android Binder机制(1/3):驱动篇

    Binder的实现是比较复杂的,想要完全弄明白是怎么一回事,并不是一件容易的事情. 这里面牵涉到好几个层次,每一层都有一些模块和机制需要理解.这部分内容预计会分为三篇文章来讲解.本文是第一篇,首先会对 ...

  7. Android Binder机制(1501210451 张志康)

    本文主要分析native层和Java层的Android binder通信机制. binder是Android最为常见的进程通信机制之一,其驱动和通信库是binder的核心,分别由C和C++编写,应用程 ...

  8. Android Binder设计与实现 - 实现篇(1)

    本文属于原创作品,转载请注明出处并放于明显位置,原文地址:http://www.cnblogs.com/albert1017/p/3849585.html 前言 在学习Android的Binder机制 ...

  9. Android Binder 分析——匿名共享内存(Ashmem)

    前面分析了 binder 中用来打包.传递数据的 Parcel,一般用来传递 IPC 中的小型参数和返回值.binder 目前每个进程 mmap 接收数据的内存是 1M,所以就算你不考虑效率问题用 P ...

  10. Android Binder 分析——数据传递者(Parcel)

    前面 binder 原理和通信模型中在接口实现部分(Bp 和 Bn)中应该看到很多地方都有使用 parcel.这个 android 专门设计用来跨进程传递数据的,实现在 native,java 层有接 ...

最新文章

  1. 网络学习:VLAN和独臂路由
  2. 791. Custom Sort String
  3. “苹果光环”褪色后,瑞声靠什么坐稳头把交椅?
  4. 从阿里中台战略看企业IT架构转型之道(上)
  5. php 设置excel格式,php 操作excel文件的方法小结
  6. 数据可视化【七】 更新模式
  7. Elasticsearch Reference [6.7] » Modules » Network Settings
  8. web学习1--web项目的WEB-INF目录
  9. libevent book——event | Gaccob的博客
  10. python画曲线图-Python数据可视化之Matplotlib(折线图)
  11. Python将字符串转换为列表
  12. 【机器学习】Pandas读取存在Github上的数据集
  13. 基于Java Web的图书管理系统
  14. Java定时任务自动调用方法
  15. 使用python turtle库绘制一个三角形_python ——turtle画三角形
  16. 虚拟机安装专用游戏多开win7系统教程简单易懂
  17. 关于互联网流量劫持分析及可选的解决方案
  18. 美女画廊(点击上面的图片下面进行显示)
  19. oralce_函数使用
  20. 服务器系统运行群晖,使用Synology的NAS系统当DNS服务器

热门文章

  1. Linux - 网络相关指令
  2. div模拟textarea自适应高度
  3. Spring MVC 复习笔记01
  4. 《101 Windows Phone 7 Apps》读书笔记-Silly Eye
  5. DeepEarth中的拖放行为(DragBehavior)
  6. J2ME开发心得-数组的使用
  7. mysql 5.6 初始化_MySQL 5.6 关于登陆的初始化设置
  8. 【Flutter】HTTP 网络操作 ( 引入 http 插件 | 测试网站 | Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用 )
  9. 【Android Protobuf 序列化】Protobuf 服务器与客户端通信 ( TCP 通信中使用 Protobuf )
  10. 【Android 应用开发】Activity生命周期 与 Activity 之间的通信