当前位置: 首页 > news >正文

Binder和IBinder

核心概念:为什么需要 Binder?

在 Android 中,应用程序(进程)是相互隔离的。一个应用不能直接访问另一个应用的内存空间。但是,系统服务(如 ActivityManagerWindowManager)运行在独立的系统进程(如 system_server)中,应用需要与它们通信;此外,应用之间也可能需要共享数据或功能。这就需要一种进程间通信(IPC) 机制

Binder 就是 Android 自己实现的、高效、安全的 IPC 机制。它相比于传统的 Linux IPC(如管道、信号量、共享内存、Socket等)具有以下优势:

  1. 高性能:只需要一次数据拷贝,性能仅次于共享内存
  2. 安全性:基于调用进程的 UID/PID 进行身份验证,支持精细的权限控制
  3. 面向对象:将 IPC 调用伪装成本地方法调用,对开发者更友好
  4. 引用计数:内置对跨进程对象生命周期的管理

一. android.os.IBinder 接口

IBinder 是 Binder 通信机制中的核心接口。它定义了跨进程通信的基本协议,是所有可以通过 Binder 进行 IPC 的对象的抽象

你可以把它理解为一个远程对象的令牌或引用。无论这个对象是在本进程还是远程进程,你都可以通过其 IBinder 引用来与它交互

主要作用:
  1. 核心 IPC 功能
    ○ transact(int code, Parcel data, Parcel reply, int flags):执行一个远程调用。这是最核心的方法
    ◎ code:用户定义的交易ID,用于标识调用哪个方法
    ◎ data:包含调用参数的 Parcel 对象
    ◎ reply:用于存放返回结果的 Parcel 对象(如果不需要回复,可以为 null)
    ◎ flags:例如 0 表示普通 RPC,FLAG_ONEWAY 表示异步调用,不等待回复
    ○ linkToDeath(IBinder.DeathRecipient recipient) 和 unlinkToDeath:注册一个“死亡通知”,当 Binder 所在进程终止时,你会收到回调。这对于清理资源非常重要
  2. 对象信息查询
    ○ String getInterfaceDescriptor():获取 Binder 对象支持的接口描述符,通常是其全限定类名
    ○ boolean isBinderAlive():检查 Binder 对象所在的进程是否还存活
  3. 查询远程接口
    ○ IInterface queryLocalInterface(String descriptor):尝试将 IBinder 对象转换回本地的 IInterface 对象。如果该 Binder 对象就在本地进程,则转换成功;如果在远程,则返回 null

总结IBinder 是一个“不透明”的通信句柄。你通常不直接使用它,而是通过由 AIDL 生成的更高级的接口来工作。但所有 Binder 对象都实现了这个接口

二. android.os.Binder 类

Binder 类是 IBinder 接口的一个具体实现。它充当了 Binder IPC 机制中的服务器端本地对象客户端远程代理的基类

当你在 AIDL 中定义一个接口时,编译工具会自动生成一个继承自 Binder 的类(例如 Stub 类)

主要角色和功能:
  1. 本地 Binder 对象(服务实现方)
    ○ 你的服务实现类(例如 MyService.Stub)会继承自 Binder
    ○ 它重写了 onTransact(int code, Parcel data, Parcel reply, int flags) 方法。这个方法负责解析客户端发来的请求(读取 data 中的参数),调用本地对应的方法,然后将结果写入 reply
    ○ 它是一个在服务端进程中的普通 Java 对象
  2. Binder 代理对象(BinderProxy)
    ○ 在客户端进程中,你拿到的实际上是一个 BinderProxy 对象。这个类是 Binder 的一个内部类,它也实现了 IBinder 接口
    ○ 当你调用代理对象的方法时(例如 aidlInterface.someMethod()),代理内部会调用 transact() 方法,将方法标识和参数打包成 Parcel,通过内核 Binder 驱动发送给服务端
    ○ BinderProxy 的 transact() 方法是 IPC 发生的客户端起点
关键方法:
  • boolean onTransact(int code, Parcel data, Parcel reply, int flags)服务端核心方法。子类必须重写此方法来处理来自客户端的请求。返回 true 表示交易成功处理
  • final boolean execTransact(...):由系统内部调用,是连接 JNI 层和 onTransact 的桥梁。通常不需要你关心
  • static final native long clearCallingIdentity() 和 static final native void restoreCallingIdentity(long token):在服务端代码中,用于清除和恢复调用方的身份。当服务端需要以其自身权限执行某些操作,而不是以客户端权限时,会用到这两个方法
  • static final native int getCallingPid() 和 static final native int getCallingUid():在服务端获取调用方的 PID 和 UID,用于权限验证

三. Binder IPC 的工作流程(结合 AIDL)

让我们通过一个典型的 AIDL 例子来看 IBinder 和 Binder 是如何协作的

1)定义 AIDL 接口 (IMyService.aidl)

interface IMyService {int add(int a, int b);
}

2)编译后生成的 Java 文件(简化版)

// 生成的接口,继承自 IInterface
public interface IMyService extends android.os.IInterface {// 客户端使用的代理类public static class Proxy implements IMyService {private android.os.IBinder mRemote; // 核心:持有一个 IBinder 引用Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}@Overridepublic int add(int a, int b) throws android.os.RemoteException {// 准备输入 Parcelandroid.os.Parcel _data = android.os.Parcel.obtain();// 准备输出 Parcelandroid.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(a);_data.writeInt(b);// !!!核心调用:通过 IBinder 发起 transact !!!mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();// 读取结果_result = _reply.readInt();} finally {_reply.recycle();_data.recycle();}return _result;}}// 服务端使用的 Stub 类,继承自 Binder,并实现了 IMyService 接口public static abstract class Stub extends android.os.Binder implements IMyService {@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {// !!!核心处理:在服务端处理 transact 请求!!!switch (code) {case TRANSACTION_add: {data.enforceInterface(DESCRIPTOR);int _arg0 = data.readInt(); // 读取参数 aint _arg1 = data.readInt(); // 读取参数 b// 调用本地实现的方法int _result = this.add(_arg0, _arg1);reply.writeNoException();// 将结果写入 replyreply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}// 将 IBinder 转换为 IMyService 接口的辅助方法public static IMyService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}// 先查询本地接口,看 obj 是否就在本进程android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof IMyService))) {// 如果是,直接返回本地对象return (IMyService) iin;}// 如果不是,则返回一个代理对象 Proxyreturn new IMyService.Proxy(obj);}}
}

3)流程总结

  1. 服务端:创建一个具体的服务对象,继承自 Stub(而 Stub 继承自 Binder),并实现 add 方法
    private final IMyService.Stub mBinder = new IMyService.Stub() {@Overridepublic int add(int a, int b) {return a + b;}
    };
    // 在 onBind 中返回这个 Binder
    @Override
    public IBinder onBind(Intent intent) {return mBinder;
    }
  2. 客户端:通过 ServiceConnection 连接到服务,在 onServiceConnected 回调中收到一个 IBinder 对象

    private IMyService myService;private ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 使用 Stub.asInterface 将 IBinder 转换为 IMyService 接口myService = IMyService.Stub.asInterface(service);}
    };
  3. 调用:客户端调用 myService.add(1, 2)
    ○ myService 实际上是一个 Proxy 对象
    ○ Proxy.add 方法将参数打包到 _data,然后调用 mRemote.transact()
    ○ mRemote 就是内核 Binder 驱动在客户端创建的 BinderProxy 对象

  4. IPCBinderProxy.transact() 通过 JNI 进入 Native 层,最终通过内核的 Binder 驱动,将请求发送到服务端进程

  5. 服务端处理:服务端进程中的 Binder 对象收到请求,其 execTransact 方法被调用,进而调用你重写的 onTransact 方法
    ○ onTransact 根据 code 识别出是 add 操作,从 data 中解包参数
    ○ 调用本地实现的 add(_arg0, _arg1) 方法
    ○ 将结果写入 reply

  6. 返回:结果沿着原路返回,内核 Binder 驱动将数据拷贝回客户端进程
    ○ 客户端的 transact() 方法返回
    ○ Proxy.add 方法从 _reply 中解包结果,并返回给客户端调用者

总结

概念角色关键点
IBinder通信协议接口定义了跨进程通信的基本操作(如 transact)。是所有 Binder 对象的根接口。代表一个远程对象的引用
Binder服务端基类 & 机制实现实现了 IBinder。作为服务端本地对象的基类(Stub),处理请求(onTransact)。其内部类 BinderProxy 是客户端的远程代理
BinderProxy客户端代理在客户端进程中,实现了 IBinder。是 transact 调用的客户端起点,负责将调用序列化并发送给驱动
AIDL工具和胶水自动生成代码(StubProxy),将复杂的 Binder/IBinder 操作封装成简单的 Java 接口调用,简化开发
http://www.dtcms.com/a/422507.html

相关文章:

  • 标准化考场建设方案解析:全频阻断作弊防控系统介绍
  • 网站开发 团队协作h5响应式 wordpress
  • 通义万相2.5系列模型发布,可生成音画同步视频
  • Transformer实战(20)——微调Transformer语言模型进行问答任务
  • Vue3和element plus在el-table中使用el-tree-select遇到的change事件坑
  • my sql 常用函数及语句的执行顺序
  • adb安装教程(附adb命令大全详解)adb环境配置教程
  • 当贝安卓9.0_创维E900S_e910V10C_3798mv310处理器线刷烧录包可救砖带adb功能
  • SQL 执行异常排查 java.sql.SQLException:从 SQLException 说起
  • uniapp 运行/发版微信小程序
  • vue2动态实现多Y轴echarts图表,及节点点击事件
  • MySQL 数据导出及备份方法
  • 公司网站建设设计如何收费网站诊断分析
  • 网站设计技巧如何看网站是用什么程序做的
  • Pythoner 的Flask项目实践-Mapboxgl-v3全球3D地图体验之地标性 3D 建筑物(迪拜哈里发大厦三维模型展示)
  • 学习机器学习要学习和掌握哪些知识?
  • 化学专业大型语言模型——SparkChemistry-X1-13B本地部署教程:洞察分子特性,精准预测化学行为
  • qt5下载
  • c 网站开发代码wordpress调用副标题
  • vscode 不能跳转 ERR_OSSL_EVP_BAD_DECRYPT
  • 大数据毕业设计选题推荐-基于大数据的全球产品库存数据分析与可视化系统-大数据-Spark-Hadoop-Bigdata
  • GitPuk入门到实战(4) - 如何进行分支管理
  • 基于AC6351D2做无线键盘
  • 【STM32项目开源】基于STM32的智能路灯控制系统
  • 超越编辑器:IntelliJ IDEA,如何成为Java开发的智慧引擎
  • Day31_【 NLP _1.文本预处理 _(2)文本张量表示方法】
  • UNIX下C语言编程与实践3-Vi 编辑器从入门到精通:快捷键使用与高效编辑技巧
  • 网站 设计 案例 简单易订货小程序怎么收费
  • 锂离子扩散能垒计算如何驱动高性能电池研发-测试GO
  • rtsoft 的“整理”流程