前言

Binder机制是Android系统提供的跨进程通讯机制,这篇文章开始会从Linux相关的基础概念知识开始介绍,从基础概念知识中引出Binder机制,归纳Binder机制与Linux系统的跨进程机制的优缺点,接着分析Binder的通信模型和原理,而Binder机制最佳体现就是AIDL,所以在后面会分析AIDL的实现原理,最后简单的提下AMS的Binder体系,整篇文章中间会穿插有IBinder、Binder、Parcel的介绍,整篇文章阅读难度不大,不会涉及到framework层的Binder原理,AIDL部分需要有AIDL的使用基础

基础概念

基础概念部分介绍Linux的某些机制,主要想表达Binder驱动的出现的原因,如果对Linux熟悉的可以直接跳过这部分,看第五点即可

一、进程隔离

出于安全考虑,一个进程不能操作另一个进程的数据,进而一个操作系统必须具备进程隔离这个特性。在Linux系统中,虚拟内存机制为每个进程分配了线性连续的内存空间,操作系统将这种虚拟内存空间映射到物理内存空间,每个进程有自己的虚拟内存空间,进而不能操作其他进程的内存空间,每个进程只能操作自己的虚拟内存空间,只有操作系统才有权限操作物理内存空间。进程隔离保证了每个进程的内存安全,但是在大多数情形下,不同进程间的数据通讯是不可避免的,因此操作系统必须提供跨进程通信机制

二、用户空间和内核空间

  • 用户空间:表示进程运行在一个特定的操作模式中,没有接触物理内存或设备的权限
  • 内核空间:表示独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限

三、系统调用/内核态/用户态

抽象来看,操作系统中安全边界的概念就像环路的概念一样(前提是系统支持这种特性),一个环上持有一个特定的权限组,Intel 支持四层环,但是 Linux 只使用了其中的两环(0号环持有全部权限,3号环持有最少权限,1号和2号环未使用),系统进程运行在1号环,用户进程运行在3号环,如果一个用户进程需要其他高级权限,其必须从3号环过渡成0号环,过渡需要通过一个安全参数检查的网关,这种过渡被称为系统调用,在执行过程中会产生一定数量的计算开销。所以,用户空间要访问内核空间的唯一方式就是系统调用(System Call)

四、内核模块/驱动

通过系统调用,用户空间可以访问内核空间,它是怎么做到访问内核空间的呢?Linux的动态可加载内核模块机制解决了这个问题,模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。这样,Android系统可以通过添加一个内核模块运行在内核空间,用户进程之间的通过这个模块作为桥梁,就可以完成通信了。在Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动

五、简单的总结

将前面的所有概念连接起来理解就会非常好消化知识点:

  1. Linux的虚拟内存机制导致内存的隔离,进而导致进程隔离
  2. 进程隔离的出现导致对内存的操作被划分为用户空间和内核空间
  3. 用户空间需要跨权限去访问内核空间,必须使用系统调用去实现
  4. 系统调用需要借助内核模块/驱动去完成

前三步决定了进程间通讯需要借助内核模块/驱动去实现,而Binder驱动就是内核模块/驱动中用来实现进程间通讯的

为什么要用Binder

Linux提供有管道、消息队列、信号量、内存共享、套接字等跨进程方式,那为什么Android要选择Binder另起炉灶呢?

一、传输性能好

  • Socket:是一个通用接口,导致其传输效率低,开销大
  • 共享内存:虽然在传输时不需要拷贝数据,但其控制机制复杂
  • Binder:复杂数据类型传递可以复用内存,需要拷贝1次数据
  • 管道和消息队列:采用存储转发方式,至少需要拷贝2次数据,效率低

二、安全性高

  • 传统的进程:通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设
  • Binder机制:从协议本身就支持对通信双方做身份校检,因而大大提升了安全性

Binder通信模型

首先在理解模型之前先熟悉这几个概念:

  • Client进程:跨进程通讯的客户端(运行在某个进程)
  • Server进程:跨进程通讯的服务端(运行在某个进程)
  • Binder驱动:跨进程通讯的介质
  • ServiceManager:跨进程通讯中提供服务的注册和查询(运行在System进程)

这里只是个简单的模型而已,只需理解模型的通讯流程:

  1. Server端通过Binder驱动在ServiceManager中注册
  2. Client端通过Binder驱动获取ServiceManager中注册的Server端
  3. Client端通过Binder驱动和Server端进行通讯

Binder通信原理

理解完模型流程之后,开始理解模型的通讯原理:

  1. Service端通过Binder驱动在ServiceManager的查找表中注册Object对象的add方法
  2. Client端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回proxy对象的add方法,add方法是个空实现,proxy对象也不是真正的Object对象,是通过Binder驱动封装好的代理类的add方法
  3. 当Client端调用add方法时,Client端会调用proxy对象的add方法,通过Binder驱动去请求ServiceManager来找到Service端真正对象,然后调用Service端的add方法

Binder对象和Binder驱动

  • Binder对象:Binder机制中进行进程间通讯的对象,对于Service端为Binder本地对象,对于Client端为Binder代理对象
  • Binder驱动:Binder机制中进行进程间通讯的介质,Binder驱动会对具有跨进程传递能力的对象做特殊处理,自动完成代理对象和本地对象的转换

由于Binder驱动会对具有跨进程传递能力的对象做特殊处理,自动完成代理对象和本地对象的转换,因此在驱动中保存了每一个跨越进程的Binder对象的相关信息,Binder本地对象(或Binder实体)保存在binder_node的数据结构,Binder代理对象(或Binder引用/句柄)保存在binder_ref的数据结构

Java层的Binder

  • Binder类:是Binder本地对象
  • BinderProxy类:是Binder类的内部类,它代表远程进程的Binder对象的本地代理
  • Parcel类:是一个容器,它主要用于存储序列化数据,然后可以通过Binder在进程间传递这些数据
  • IBinder接口:代表一种跨进程传输的能力,实现这个接口,就能将这个对象进行跨进程传递
  • IInterface接口:client端与server端的调用契约,实现这个接口,就代表远程server对象具有什么能力,因为IInterface接口的asBinder方法的实现可以将Binder本地对象或代理对象进行返回

Binder类和BinderProxy类都继承自IBinder,因而都具有跨进程传输的能力,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分,但它不仅用于远程调用,也用于进程内调用。IBinder接口定义了与远程对象交互的协议,建议不要直接实现这个接口,而应该从Binder派生。Binder实现了IBinder接口,但是一般不需要直接实现此类,而是跟据你的需要由开发包中的工具生成,这个工具叫aidi。你通过aidi语言定义远程对象的方法,然后用aidi工具生成Binder的派生类,然后使用它

AIDL

由于编译工具会给我们生成一个Stub的静态内部类,这个类继承了Binder, 说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有远程Server承诺给Client的能力

一、服务端

在服务端中,我们只要实现Stub抽象类,和实现其方法即可

private IBinder myS = new IMyAidlInterface.Stub() {  @Override  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {  }  @Override  public int add(int num1, int num2) throws RemoteException {  Log.i("Hensen", "从客户端发来的AIDL请求:num1->" + num1 + "::num2->" + num2);  return num1 + num2;  }
}; 

二、客户端

在客户端中,可以通过bindService的回调中获取AIDL接口

private ServiceConnection conn = new ServiceConnection() {  @Override  public void onServiceConnected(ComponentName name, IBinder service) {  iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);  }  @Override  public void onServiceDisconnected(ComponentName name) {  iMyAidlInterface = null;  }
};public void add(View view) {  try {  int res = iMyAidlInterface.add(1, 2);  Log.i("Hensen", "从服务端调用成功的结果:" + res);  } catch (RemoteException e) {  e.printStackTrace();}
}   

梳理客户端的调用流程:

  1. 调用Stub.asInterface获取BinderProxy对象
  2. 调用BinderProxy对象的add方法

三、分析原理

1、Stub

Stub类继承自Binder,意味着这个Stub其实自己是一个Binder本地对象,然后实现了IMyAidlInterface接口,IMyAidlInterface本身是一个IInterface,因此他携带某种客户端需要的能力(这里是方法add)。此类有一个内部类Proxy,也就是Binder代理对象

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: D:\\workspace5\\Boke\\app\\src\\main\\aidl\\com\\handsome\\boke\\IMyAidlInterface.aidl */
package com.handsome.boke;
// Declare any non-default types here with import statements  public interface IMyAidlInterface extends android.os.IInterface {  /** * Local-side IPC implementation stub class. */  public static abstract class Stub extends android.os.Binder implements com.handsome.boke.IMyAidlInterface {  private static final java.lang.String DESCRIPTOR = "com.handsome.boke.IMyAidlInterface";  /** * Construct the stub at attach it to the interface. */  public Stub() {  this.attachInterface(this, DESCRIPTOR);  }  /** * Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface, * generating a proxy if needed. */  public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {  if ((obj == null)) {  return null;  }  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {  return ((com.handsome.boke.IMyAidlInterface) iin);  }  return new com.handsome.boke.IMyAidlInterface.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 {  switch (code) {  case INTERFACE_TRANSACTION: {  reply.writeString(DESCRIPTOR);  return true;  }  case TRANSACTION_basicTypes: {  data.enforceInterface(DESCRIPTOR);  int _arg0;  _arg0 = data.readInt();  long _arg1;  _arg1 = data.readLong();  boolean _arg2;  _arg2 = (0 != data.readInt());  float _arg3;  _arg3 = data.readFloat();  double _arg4;  _arg4 = data.readDouble();  java.lang.String _arg5;  _arg5 = data.readString();  this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);  reply.writeNoException();  return true;  }  case TRANSACTION_add: {  data.enforceInterface(DESCRIPTOR);  int _arg0;  _arg0 = data.readInt();  int _arg1;  _arg1 = data.readInt();  int _result = this.add(_arg0, _arg1);  reply.writeNoException();  reply.writeInt(_result);  return true;  }  }  return super.onTransact(code, data, reply, flags);  }  private static class Proxy implements com.handsome.boke.IMyAidlInterface {  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;  }  /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */  @Override  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {  android.os.Parcel _data = android.os.Parcel.obtain();  android.os.Parcel _reply = android.os.Parcel.obtain();  try {  _data.writeInterfaceToken(DESCRIPTOR);  _data.writeInt(anInt);  _data.writeLong(aLong);  _data.writeInt(((aBoolean) ? (1) : (0)));  _data.writeFloat(aFloat);  _data.writeDouble(aDouble);  _data.writeString(aString);  mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);  _reply.readException();  } finally {  _reply.recycle();  _data.recycle();  }  }  @Override  public int add(int num1, int num2) throws android.os.RemoteException {  android.os.Parcel _data = android.os.Parcel.obtain();  android.os.Parcel _reply = android.os.Parcel.obtain();  int _result;  try {  _data.writeInterfaceToken(DESCRIPTOR);  _data.writeInt(num1);  _data.writeInt(num2);  mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  _reply.readException();  _result = _reply.readInt();  } finally {  _reply.recycle();  _data.recycle();  }  return _result;  }  }  static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);  }  /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;  public int add(int num1, int num2) throws android.os.RemoteException;
}  

2、asInterface

当客户端bindService的onServiceConnecttion的回调里面,通过asInterface方法获取远程的service的。其函数的参数IBinder类型的obj,这个对象是驱动给我们的,如果是Binder本地对象,那么它就是Binder类型,如果是Binder代理对象,那就是BinderProxy类型。asInterface方法中会调用queryLocalInterface,查找Binder本地对象,如果找到,说明Client和Server都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回,如果找不到,说明是远程对象那么就需要创建Binder代理对象,让这个Binder代理对象实现对于远程对象的访问

/** * Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface, * generating a proxy if needed. */
public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {  if ((obj == null)) {  return null;  }  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {  return ((com.handsome.boke.IMyAidlInterface) iin);  }  return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);
}

3、add

当客户端调用add方法时,首先用Parcel把数据序列化,然后调用mRemote.transact方法,mRemote就是new Stub.Proxy(obj)传进来的,即BinderProxy对象

@Override
public int add(int num1, int num2) throws android.os.RemoteException {  android.os.Parcel _data = android.os.Parcel.obtain();  android.os.Parcel _reply = android.os.Parcel.obtain();  int _result;  try {  _data.writeInterfaceToken(DESCRIPTOR);  _data.writeInt(num1);  _data.writeInt(num2);  mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  _reply.readException();  _result = _reply.readInt();  } finally {  _reply.recycle();  _data.recycle();  }  return _result;
}  

4、transact

BinderProxy的transact方法是native方法,它的实现在native层,它会去借助Binder驱动完成数据的传输,当完成数据传输后,会唤醒Server端,调用了Server端本地对象的onTransact函数

public native boolean transact(int code, Parcel data, Parcel reply,int flags) throws RemoteException;

5、onTransact

在Server进程里面,onTransact根据调用code会调用相关函数,接着将结果写入reply并通过super.onTransact返回给驱动,驱动唤醒挂起的Client进程里面的线程并将结果返回

@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_add: {  data.enforceInterface(DESCRIPTOR);  int _arg0;  _arg0 = data.readInt();  int _arg1;  _arg1 = data.readInt();  int _result = this.add(_arg0, _arg1);  reply.writeNoException();  reply.writeInt(_result);  return true;  }  }  return super.onTransact(code, data, reply, flags);
}  

6、题外话

为什么生成的文件不直接分为1个接口,2个类,清晰明了。Android这样子设计是有道理的,当有多个AIDL文件时候,Stub和Proxy类就会重名,把它们放在各自的AIDL接口中,就区分开来了

AMS的Binder体系

AMS是Android中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,从图中可以看出:

AMS Binder角色
IActivityManager IInterface
ActivityManagerNative Binder本地对象
ActivityManagerProxy Binder代理对象
ActivityManagerService Service端
ActivityManager 普通管理类

结语

这里只是简单的理解下Binder机制的基本原理,后续有时间会研究framework层的知识,如果有兴趣的同学可以不依赖AIDL工具,手写远程Service完成跨进程通信,这样就可以加深对AIDL和Binder的理解,个人觉得这样是最好的记忆方式,一起来写吧

Android进阶——Android跨进程通讯机制之Binder、IBinder、Parcel、AIDL相关推荐

  1. Android跨进程通讯机制之Binder、IBinder、Parcel、AIDL

    https://blog.csdn.net/qq_30379689/article/details/79451596 前言 Binder机制是Android系统提供的跨进程通讯机制,这篇文章开始会从L ...

  2. Android开发之跨进程通讯-AIDL实现方法 (附源码)

    先看效果图,下面是广播和AIDL跨进程的方法 我们先创建AIDL文件定义接口方法 定义好接口方法如下图: // ITokenAidlInterface.aidl package com.example ...

  3. Android 进阶8:进程通信之 Binder 机制浅析

    读完本文你将了解: IBinder Binder Binder 通信机制 Binder 驱动 Service Manager Binder 机制跨进程通信流程 Binder 机制的优点 总结 Than ...

  4. Android ContentProvider实现两个程序间数据共享demo,跨进程通讯

    1.客户端代码: 先实现服务端 SQL创建: public class DBHelper extends SQLiteOpenHelper {// 数据库名private static final S ...

  5. Android AILD跨进程通讯 解决bindService无法启动

    场景:APP-A 服务端,APP-B 客户端.B启动service,通过IBinder,拿到服务端User列表,并向User表中添加人员信息.从而实现两个APP跨进程通讯 一,服务端APP 步骤:1, ...

  6. Android 跨进程通讯的方式

    我已知Android 的跨进程通信方式有6种,分别为:访问他应用的Activity.接收其他应用的广播.访问其它应用的 开放数据.AIDL.Messenger和socket的跨进程通信. (1)访问他 ...

  7. linux 跨进程读取内存,Android之Linux跨进程通信的方式

    As we all know,Android是基于Linux内核开发的,而市面上几乎所有的App都离开跨进程通信.可能你会说Android是通过Binder完成进程之间的通信的.但是Binder是怎么 ...

  8. Android进阶——Android四大组件启动机制之Activity启动过程

    前言 Activity启动过程涉及到的比较多的知识点有Binder的跨进程通讯,建议先看完Binder的跨进程通讯再来阅读本篇文章,在文章阅读开始,我们先要理解Activity启动模型,再者去理解有关 ...

  9. Android ContentProvider支持跨进程数据共享与互斥、同步 杂谈

    在开发中,假如,A.B进程有部分信息需要同步,这个时候怎么处理呢?设想这么一个场景,有个业务复杂的Activity非常占用内存,并引发OOM,所以,想要把这个Activity放到单独进程,以保证OOM ...

最新文章

  1. java程序包不存在_第一章 Java语言简介
  2. 2021 年 ICT 行业预测
  3. 积跬步,聚小流-------js实现placeholder的效果
  4. android 一个字符串分两行显示_重新梳理Android权限管理
  5. 使用OpenCV,Keras和Tensorflow构建Covid19掩模检测器
  6. 局域网内数据采集总结(四)
  7. 这样的开源方式,你喜欢吗?
  8. oracle left join行数,sql – 如何将此LEFT JOIN返回的行数限制为一个?
  9. 前端操作复制粘贴板(clicpboardData )
  10. 一、什么是统一社会信用代码
  11. bin文件合并工具(UBIN)使用方法
  12. 你好,CSDN,我来了。我想在这里记录我的学习和分享知识教程
  13. H5 iOS 自动调起软键盘
  14. 深入linux内核架构--虚拟文件系统VFS
  15. ue4 后期处理景深_Unreal Engine4 后期处理特效 VOL1
  16. Java基础入门必须知道的英语词汇
  17. OnePlus 7T LineageOS 编译向导
  18. java实训小项目6_实训项目
  19. c语言计算时钟的夹角不用if,C语言学习笔记——计算时钟的夹角
  20. 简简单单右键菜单-闲情偶寄-iteye技术网站

热门文章

  1. SpringBoot+vue项目初级(一)
  2. 多个PDF合并成一个后大小不一致?教你一招
  3. matlab 不同函数间传递结构体数据_matlab 结构体struct函数使用方法
  4. network system
  5. 【转载】MSE(均方误差)、RMSE (均方根误差)、MAE (平均绝对误差)- 机器学习 - 线性回归之模型评估
  6. 拍森 | numpy库
  7. php泥浆是什么,深厚砂性土层钻孔桩中PHP泥浆应用
  8. 服务器双向认证 原理,https认证方式以及HTTPS双向认证过程
  9. 华三交换机清除配置命令
  10. 【C#】 Convert.ToInt16 、Convert.ToInt32、Convert.ToInt64 区别