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

article/2025/9/2 9:14:13

前言

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中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,从图中可以看出:

AMSBinder角色
IActivityManagerIInterface
ActivityManagerNativeBinder本地对象
ActivityManagerProxyBinder代理对象
ActivityManagerServiceService端
ActivityManager普通管理类

结语

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


http://chatgpt.dhexx.cn/article/CDRnkS1q.shtml

相关文章

[转]IBinder对象在进程间传递的形式

目录: 问题引出:由onServiceConnected()的IBinder类型说起IBinder传递时Driver Module的处理IBinder传递时framework的处理发送IBinder接收IBinder 问题引出:由onServiceConnected()的IBinder类型说起 当service被远程调用时,我们…

android i 网络接口,从IBinder接口学习Proxy-Stub设计模式

定义了抽象函数,如上图里的onTransact()函数,成为提供给众多子类别来实现的内部接口。我们以来表示之。 如下图所示: 图3、基类实现CI,并定义 ---- 由子类来实现内部接口。Binder基类的很重要目的是支持跨进程调用Service&#xf…

Android IPC 之获取服务(IBinder)

前言 IPC 系列文章: 建议按顺序阅读。 Android IPC 之Service 还可以这么理解 Android IPC 之Binder基础 Android IPC 之Binder应用 Android IPC 之AIDL应用(上) Android IPC 之AIDL应用(下) Android IPC 之Messenger 原理及应用 Android IPC 之服务端回调 Android…

android ibinder 机制,Android IBinder机制简单介绍

原理简介 我们都知道android 是通过IBinder来实现IPC(Inter Process Communication)进程间通信的。。。 借用一下: 1. Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中 2. Binder驱动程序和Service Manager在Android平台中已经实现,开发者…

sql之嵌套查询中的带exists谓词的子查询

数据库系统概论之嵌套查询中的带exists谓词的子查询 一、exists谓词概述:exists谓词代表存在量词。带有exists谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。可以利用exists来判断属性与关系表之间的属于关系,关系…

sql in和exist

前言 最近写SQL的时候要求在排除A表中所存在的B表的某些同属性值的记录。 然后就想到了in和exist,但是其实一直都没有真正的去弄懂两者不同, 于是在网上查询了一些大佬的文章,然后去实践了一番,最后做一个总结 开始啰 1&#xff…

SQL之EXISTS的理解

将外查询表的每一行,代入内查询作为检验,如果内查询返回的结果取非空值,则EXISTS子句返回TRUE,这一行行可作为外查询的结果行,否则不能作为结果。 先上表 过程:外查询Persons表,提取出数据到E…

sql中exists的常用用法

exists中子查询结果集非空,则exists子查询返回true。如果exists子查询结果集为空,则exists子查询返回false。在平常的开发工作中,经常会用到exists,那么它应该如何使用呢? 1:查询兴趣爱好为跳舞的同学姓名及…

MySQL SQL语句EXISTS

MySQL中EXITS语句用于查明表中是否存在特定的行。普遍情况下EXITS与子查询一起使用,并返回与子查询返回的结果相等或匹配的行。如果行在表中存在,则返回true,否则返回false。在MySQL中使用EXISTS是低效的,因为EXISTS对查理表中的每…

SQL语句中EXISTS的用法

SQL萌新一个,在这里记录一下自学过程中遇到的问题。 exists:强调的是,是否有返回集,不需要知道具体返回的是什么 比如这两个表: 输入查询语句: select * from customer c where not exists( select * from…

sql中 exists的用法

现有:班级表(A_CLASS) 学生表( STUDENT) 注:学生表(STUDENT)的classId关联班级表(A_CLASS)的主键ID 代码: select * from STUDENT s WHERE exists (select 1 from A_ClASS c where s.CLASS_…

SQL语句中EXISTS的详细用法大全

SQL语句中EXISTS的详细用法大全 前言一、建表1.在MySQL数据库建表语句2.在ORACLE数据库建表语句 二、在SELECT语句中使用EXISTS1.在SQL中使用EXISTS2.在SQL中使用NOT EXISTS3.在SQL中使用多个NOT EXISTS4.在SQL中使用多个EXISTS5.在SQL中使用NOT EXISTS和EXISTS 三、在DELETE语…

DNS服务器解析问题

DNS服务器解析问题 前言 重点: 本文摘选链接:https://www.ancii.com/aazeaa674/ 正文 问题 域名状态异常会导致网站不能访问吗? 刚修改过域名解析,为什么不生效呢? 如何查看解析是否生效呢? 刚在注…

dns服务器怎么设置

dns服务器怎么设置 在修改DNS之前需要先知道你的大DNS服务器地址是什么,那么怎么来查询DNS服务器地址呢?直接按住键盘上的“winR”,调出运行框。在输入框中输入“cmd”,点击“确定”或者回车。在管理员界面中输入命令:…

手机显示DNS服务器异常,手机dns服务器异常怎么设置

手机dns服务器异常怎么设置 内容精选 换一换 华为云帮助中心,为用户提供产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题、视频帮助等技术文档,帮助您快速上手使用华为云服务。 本章节介绍如何通过控制台重启服务器。重启服务器时,可以批量更换云手机…

小米路由器显示DNS服务器设置错误,小米路由器dns地址怎么设置

小米路由器与日常所有的路由器一样,在默认情况下都是路由器的地址作为无线设备获取的 DNS 地址。如果你想手动修改小米路由器的DNS地址,也是可以的,下面是学习啦小编给大家整理的一些有关小米路由器dns地址设置方法,希望对大家有帮…

如何设置正确的dns服务器地址,dns服务器地址如何设置

dns服务器地址如何设置 (Windows 2000、Windows XP操作系统): 1、 开机后在桌面上选定图标"网上邻居", 点击鼠标右键, 在弹出的菜单上选择"属性"项,打开"网络和拔号连接"窗口, 如图 2、…

北京联通dns服务器位置,联通DNS服务器地址怎么设置

联通DNS服务器地址怎么设置 如电脑Win7系统,以下方法设置DNS:您右键点击电脑桌“网络”图标 ,“属性” >选择“控制面板” >在“网络和共享中心”中可看到当前的网络状况,点击左边的“更改适配器设置” >右键单击“本地连接”择“属性” >选择“internet协议版本4(TCP/…

路由器显示DNS服务器设置错误,路由器dns设置错误怎么处理

一般都给家里配上了无线路由器,但是在用了无线路由器后,很容易导致电脑断网,大家一般用软件检测后上面会显示是DNS地址出现了异常,那么应该怎么解决这个问题,不让电脑频繁断网呢?下面是学习啦小编给大家整理的一些有关…

无线路由dns服务器地址,tplink无线路由器怎么设置DNS服务器地址

作为不同网络之间互相连接的枢纽,路由器系统构成了基于TCP/IP 的国际互联网络Internet 的主体脉络,也可以说,路由器构成了Internet的骨架。在设置路由器的时候,很多用户不知道该怎么手动设置路由器的DNS服务器地址,下面我们就来看看详细的设置教程,需要的朋友可以参考下 …