1.背景

在前面的博客中,我们已经学会了使用AIDL进行跨进程通信,AIDL的使用比较简单,可实际上跨进程通信是一个相当复杂的过程,例如进程A是怎么找到进程B的,如果有一个进程C冒充进程A,进程B又该如何识别等等问题,而使用AIDL时,完全不用关心这复杂的过程,开发者只需关注业务逻辑即可,有句话说,哪有什么岁月静好,只不过有人替我们负重前行,AIDL的背后肯定有机制帮我们完成了这些进程间通讯的复杂操作,这个机制便是Binder。

Binder中文意思是黏合剂,这个名称很贴切,生活中的黏合剂是将两种材料通过界面的粘附和内聚强度连接在一起的物质,而Android系统里,Binder则是将两个进程间黏合一起实现通讯。

今天就来了解一下Binder。

2.Binder发展历史

Binder是Android系统里进程间通信机制,源于OpenBinder,OpenBinder进程间通信机制最早由Be公司提出,后来这个机制被Palm操作系统所采用,第一个版本也是在Palm的微内核系统上实现,后来Palm改用Linux操作系统,OpenBinder也跟着移植到Linux并且开源,而Android系统基于Linux加上Google聘请了OpenBinder关键工程师Dianne Hackborn加入Android团队,因此OpenBinder自然而然并入Android系统,最初版本的Android时按原样使用OpenBinder,随后改名成Binder并完全重写,成了今天Binder的样子,值得一提的是,OpenBinder已经不再维护,算是寿终正寝,但其机制被Binder所继承,从某种意义上来讲,OpenBinder并未消亡,而是以Binder的形态继续传承下去。

3.为什么使用Binder

Android系统基于Linux,而Linux系统本身具有多种进程间通信方式可供选择,例如文件,Socket,Pipes,消息队列等,为何陪嫁过来的Binder能够后来居上,成为Android系统里进程间通信的首选呢?

IPC要考虑的因素包括易用,安全,性能,Linux提供的原始IPC选项,易用性好的性能较差,例如Socket作为Client-Service通信方式,开发者使用较方便,但效率低,用于设备间通信较合适,用于进程间通信则臃肿了;性能高的易用性和安全性较差,性能越高,往往越处于底层,底层的接口往往需要开发者考虑许多因素,例如在进程终止时需及时释放资源,否则容易发生内存泄漏,而Binder则很好平衡了这三个因素:

  1. Binder通过共享内存实现高性能,Linux原生的消息队列和管道需数据先从发送方拷贝到内存开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程,而Binder通过共享内存,只需要拷贝一次;
  2. Binder通过UID/PID管理发送者和接收者(通过UID / PID),提升了安全性,而Liunx传统IPC没有安全错误,依赖开发者自己控制;
  3. 使用简单的AIDL开发,保证了易用性,对开发者友好

Binder还有其他好处,包括原生支持多种数据类型,自定义类型使用过程也不复杂,支持同步和异步,自动回收资源等等。

Binder也因为优秀的特性,已深深的和Android融合在一起,正如Hackborn自己所说的,在Android系统上,Binder几乎用于所有跨进程的操作。如果关掉Binder,整个系统将会是没显示,没声音,没输入,处于几乎无法运作的状态,Binder对Android系统的重要性可见一斑。

3.Binder工作机制

一个客户端即进程A想和一个服务端即进程B交互,由于进程间的隔离,客户端无法直接调用服务端,但内核可以,因此他们使用Binder驱动捎话,传递消息。用日常生活的例子来说,进程A是一个有多余储蓄,想投资赚点收益投资者,进程B是一个急着用钱的借款人,两人并不熟悉,由于信息不对称,投资者不会直接借钱给借款人,因此也需要一个金融中介,对应于Binder驱动。

由于Binder驱动在内核层,内核层的接口协议比较繁琐,客户端和服务端并不想了解底层协议,只想专注于业务开发,于是他们使用代理(客户端是Proxy,服务端是Stub)来和Binder驱动打交道,还是用金融中介概念理解,投资人当然可以直接和金融中介例如银行打交道,但越底层的业务办起来越罗嗦,例如银行上班时间和我们一样,我们下班了他们也下班,想办点业务还得请假,去到营业厅还得排队,还需要填各种表格,这时候可以找代理帮忙跑腿,省了很多事。

客户端的代理Proxy和服务端的代理Stub不用开发者自己实现,而是依靠AIDL工具,回忆一下AIDL的使用过程,当我们新建一个AIDL文件时,Android SDK工具根据AIDL的描述,自动生成一个同名的java文件,java文件中就包含了Proxy和Stub,以及与Binder驱动通信的代码。这也是为什么反复强调,当更新AIDL文件时,一定要及时同步工程,因为Android SDK工具可能不及时更新同名java文件。

最后一个问题是,Android系统提供的服务端多种多样,客户端怎么联系指定的服务端,这需要依靠Context Manager,Context Manager提供服务的登记和查询功能,类似于现实生活中的银保监会,金融机构需要在银保监会备案,在Android系统中,使用Service Manager实现Context Manager功能,Service Manager是所有服务中第一个启动的,其启动后,其他服务才能登记注册。

服务的向Context Manager注册和客户端向Context Manager查询服务的过程,其实也是一个跨进程通信过程,那么服务端又怎么知道Context Manager的句柄?难道又要弄一个管理Context Manager的死循环过程吗,这倒是没必要,给予Context Manager一个固定的地址即可。过程见下图。

4.AIDL源码分析

仍以上篇博客创建的AIDL工程为例子(见Android AIDL使用介绍(2)),在该工程中,创建了AIDL例子,Android Studio自动在对应的

build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\包名

目录下生成对应的同名java文件,路径见图

打开看一下里面的内容,代码一开始就有注释提醒我们,这是自动生成的代码,请勿修改,因为是自动生成的代码,可读性上稍微差一点。

/** This file is auto-generated.  DO NOT MODIFY.*/
package com.pm.service;
public interface ServiceAidlInterface extends android.os.IInterface
{/** Default implementation for ServiceAidlInterface. */public static class Default implements com.pm.service.ServiceAidlInterface{@Override public java.lang.String ServiceGreet() throws android.os.RemoteException{return null;}@Override public java.util.List<com.pm.service.Student> getStudentList() throws android.os.RemoteException{return null;}@Overridepublic android.os.IBinder asBinder() {return null;}}/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.pm.service.ServiceAidlInterface{private static final java.lang.String DESCRIPTOR = "com.pm.service.ServiceAidlInterface";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.pm.service.ServiceAidlInterface interface,* generating a proxy if needed.*/public static com.pm.service.ServiceAidlInterface asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.pm.service.ServiceAidlInterface))) {return ((com.pm.service.ServiceAidlInterface)iin);}return new com.pm.service.ServiceAidlInterface.Stub.Proxy(obj);}@Override public android.os.IBinder asBinder(){return this;}@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{java.lang.String descriptor = DESCRIPTOR;switch (code){case INTERFACE_TRANSACTION:{reply.writeString(descriptor);return true;}case TRANSACTION_ServiceGreet:{data.enforceInterface(descriptor);java.lang.String _result = this.ServiceGreet();reply.writeNoException();reply.writeString(_result);return true;}case TRANSACTION_getStudentList:{data.enforceInterface(descriptor);java.util.List<com.pm.service.Student> _result = this.getStudentList();reply.writeNoException();reply.writeTypedList(_result);return true;}default:{return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.pm.service.ServiceAidlInterface{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 java.lang.String ServiceGreet() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.lang.String _result;try {_data.writeInterfaceToken(DESCRIPTOR);boolean _status = mRemote.transact(Stub.TRANSACTION_ServiceGreet, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().ServiceGreet();}_reply.readException();_result = _reply.readString();}finally {_reply.recycle();_data.recycle();}return _result;}@Override public java.util.List<com.pm.service.Student> getStudentList() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<com.pm.service.Student> _result;try {_data.writeInterfaceToken(DESCRIPTOR);boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().getStudentList();}_reply.readException();_result = _reply.createTypedArrayList(com.pm.service.Student.CREATOR);}finally {_reply.recycle();_data.recycle();}return _result;}public static com.pm.service.ServiceAidlInterface sDefaultImpl;}static final int TRANSACTION_ServiceGreet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);public static boolean setDefaultImpl(com.pm.service.ServiceAidlInterface impl) {if (Stub.Proxy.sDefaultImpl == null && impl != null) {Stub.Proxy.sDefaultImpl = impl;return true;}return false;}public static com.pm.service.ServiceAidlInterface getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}public java.lang.String ServiceGreet() throws android.os.RemoteException;public java.util.List<com.pm.service.Student> getStudentList() throws android.os.RemoteException;
}

代码的层次分布见下图

  • 首先是定义抽象方法的声明,这些函数就是我们在aidl文件定义的抽象方法,包括ServiceGreet()getStudentList()
  • 其次是抽象方法的缺省实现,在AIDL工程中,我们已经在Service里实现了抽象方法,假如没有实现的话,则使用自动生成的缺省实现;
  • 再次是内部类Stub,Stub是Binder的子类,跨进程调用由这一个内部类完成,当客户端和服务端不在同一个进程时,走transaction(交易)过程,在当前语境下,transaction指的是一个进程传递数据给另一个进程,传递的数据按照一定的格式要求组包,transaction的业务逻辑由Stub的内部代理类Proxy来完成;
  • 接下来便是onTransact(int code, Parcel data, Parcel reply, int flag),这是处理transaction的具体逻辑,这也是客户端和服务端直接沟通的渠道,当客户端发起请求时,这个方法来处理请求,服务端通过code获取客户端想要访问的目标方法,这个code是方法的编号,代码中已经声明
    static final int TRANSACTION_ServiceGreet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    

    第二个参数data用于获取目标方法所需的参数,注意是Parcel类型,当服务端执行完目标方法后,将返回值写入到reply中;

  • 其他重要接口如asInterface()用于判断当前进程是服务端进程还是客户端进程,如果是服务端进程则返回Stub对象,否则返回Stub.Proxy对象;DESCRIPTOR用于表示Binder的唯一标识,使用当前当前Binder的包路径表示;asBinder()返回当前Binder对象。

4.总结

本文主要分析AIDL背后使用到的进程间通信机制Binder,Binder源自于OpenBinder,后来随作者陪嫁到Android系统,凭借着易用性好,性能高,安全性能好,后来居上,成为Android进程间通信机制,成为Android不可或缺的一部分。

Binder的机制细节其实还有很多,本文只是尝试简化说明,如有错漏,欢迎指正。

参考目录:

1.Deep Dive into Android IPC/Binder Framework at Android Builders Summit 2013;
2.Android’s Binder – in depth
3.https://www.cnblogs.com/zc9527/p/5638688.html;
4.https://www.cnblogs.com/itgungnir/p/6640120.html

Android AIDL使用介绍(3) 浅说AIDL背后的Binder相关推荐

  1. Android AIDL使用介绍(2)自定义数据类型的传递

    1.背景 默认情况下,AIDL只支持下列数据类型: Java八种基础数据类型(如 int.long.char.boolean 等); String字符串: CharSequence字符序列: List ...

  2. Android AIDL使用介绍(1)基本使用

    1.什么是AIDL AIDL全称是Android Interface Definition Language,中文译为Android接口定义语言,AIDL的提出是为了解决进程间通讯,我们知道,在And ...

  3. [Android]你不知道的Android进程化(4)--进程通信AIDL框架

    Google爸爸,听说要将一些插件化hook系统的变量属性禁用,Android P之后很可能将会不再有插件化.热更新.主题变换.资源加固等骚操作.试图hook,你将会看到 NoSuchFieldExc ...

  4. aidl远程服务调用Android,报错:Process 'command 'F:\Android\SDK\build-tools\29.0.0\aidl.exe''

    aidl远程服务调用Android demo1: Alipay 支付App服务: 1.新建: Alipay\app\src\main\aidl\com\glsite\alipay\IAlipaySer ...

  5. AIDL 详细介绍及使用

    一.AIDL是什么? AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语 ...

  6. 【Android Gradle 插件】AndroidSourceSets 配置 ③ ( aidl 配置 | assets 配置 | compileConfigurationName 配置 )

    文章目录 一.AndroidSourceSets#aidl 配置 二.AndroidSourceSets#assets 配置 三.AndroidSourceSets#compileConfigurat ...

  7. 关于 Android Service 的介绍都在这了

    概述 Service 是 Android 的四大组件之一,它主要的作用是后台执行操作,Activity 属于带有 UI 界面跟用户进行交互,而 Service 则没有 UI 界面,所有的操作都是基于后 ...

  8. ANDROID SDK体系介绍

    本章介绍了Android SDK的相关内容,包括其文档解读,示例讲解,以及相关API的介绍,通过本章的学习,可以比较清晰地把握Android SDK的全貌,熟悉其提供的相关示例,以及附带的工具使用.另 ...

  9. android驱动框架介绍

    android驱动框架介绍 了解android驱动框架: 1.方法1--jni调用底层驱动 在android框架中写入c/c++直接调用底层linux驱动,并向上提供jni接口给应用程序: 优点:简单 ...

最新文章

  1. 如何初始化一个定长ListT
  2. wxWidgets:wxFont概览
  3. crc32库 qt_Qt 打包32位库(包括WebEngineView)
  4. string replace
  5. Bash Shell学习笔记四
  6. Shell shift的使用方法
  7. django-多级联动-前端效果
  8. 被尘封的故事技能点bug_王者荣耀体验服更新,多名英雄技能优化,瑶妹玩家却坐不住了...
  9. 数据结构—单链表(类C语言描述)
  10. Python菜鸟入门:day08函数概念
  11. 织梦怎样调取mysql_如何实现dedecms外部数据库调用
  12. 1046 划拳 (15 分)—PAT (Basic Level) Practice (中文)
  13. CVPR 2020放榜,录取率降至22%,港中文周博磊发文感慨十年变迁
  14. 中古调式(调式音阶) 二
  15. 小程序人脸识别 图片转换成base64 上传给后台
  16. Netd 中 NetworkManager 分析
  17. dataframe 查找的isin()用法
  18. 注册表怎么打开详细教程
  19. 我花了20年研究华为,发现了这些秘密……
  20. python打印pdf特定页面_使用Python自由切分pdf文件提取任意页面

热门文章

  1. boost::all_clustering_coefficients用法的测试程序
  2. boost::fusion::make_fused用法的测试程序
  3. boost::container模块实现抛出异常
  4. Boost:bind绑定的unique_ptr测试程序
  5. DCMTK:测试CT Table Dynamics FG类
  6. VTK:Utilities之Animation
  7. VTK:Snippets之ReadPolyData
  8. VTK:PolyData之PointLocator
  9. VTK:网格之WindowedSincPolyDataFilter
  10. QT通过JavaScript动态创建QML对象