libbinder_ndk 入门指南

简介

替换libbinder, 也被称为Stable AIDL,在 Android 10 中引入的 官方推荐 Binder 后端。

  • ABI 稳定: 它通过一套精简的 C API(即 android/binder_... 系列函数)屏蔽了底层原生 libbinder 的复杂实现。
  • 跨分区通信: 它是 /vendor 分区与 /system 分区进行跨层通信(VINTF)的唯一标准。
  • 现代 C++ 体验: 虽然底层是 C API,但它在 ndk:: 命名空间下提供了一套极其丝滑的 C++ 包装(RAII 指针、SharedRefBase 等)。

关键组成部分

  • C API 层 :最底层的函数,如 AIBinder_transact。它们是跨版本兼容的"硬通货"。
  • C++ 辅助类 :练习的重心:SpAIBinder (强指针)、WpAIBinder (弱指针)、ScopedAStatus (错误管理)。
  • 接口辅助类:提供 SharedRefBase,解决 C++ 对象与 Binder 驱动引用计数同步的难题。

AIDL 工具生成的 Bn...(服务端)和 Bp...(客户端代理)。

实现与使用

服务端

  1. 定义AIDL文件,文件名字和定义里面"类"名字保持一致,且以I开头
  2. 服务端实现BnXxx基类,去掉AIDL名字的开头I
  3. 生成binder对象(SharedRefBase::make)
  4. 添加到服务中(AServiceManager_addService),或者转换给java层(AIBinder_toJavaBinder)
  5. C/C++程序,设置binder线程模式参数
  • 普通模式:设置线程数,启动线程并加入循环
  • 单线程或者集成模式:获取文件描述符、监听文件处理、自循环处理

客户端

  1. 获取服务Binder(AServiceManager_getService/AIBinder_fromJavaBinder)
  2. 转换对象: AIDL文件名::fromBinder(binder)

四种指针

  1. SharedRefBase:确保 C++ 对象在还有远程 Binder 调用时不会被销毁,而在没人使用时能自动释放内存。
  • 对象实例化:ndk::SharedRefBase::make
  • 类内部安全获取:ref(),返回shared_ptr; weak_ref(), 返回weak_ptr
  • 转换binder: asBinder(), 换成NDK层的SpAIBinder
  1. SpAIBinder:对应于AIDL定义中的IBinder, 用于传递IBinder对象;get方法获取裸指针
  2. WpAIBinder:破解循环使用,缓存对象且不占用资源; 使用promote提升为SpAIBinder;构造时可以接受裸指针,也可以是SpAIBinder,建议是SpAIBinder
  3. 自动管理句柄:ScopedAResource实现类,专门用于管理那些需要手动删除的辅助对象;get/set/release/构造,其它方法看各个实现类,常用有下面三个类
  • ScopedAStatus-->AStatus
  • ScopedAIBinder_DeathRecipient-->AIBinder_DeathRecipient
  • ScopedAParcel-->AParcel

AStatus

binder_status头文件中,负责创建、检查和销毁binder的执行结果

  1. 创建状态
  • AStatus_newOk(): 创建一个表示"成功"的状态。
  • AStatus_fromExceptionCode(exception_code): 从标准异常码(如 EX_SECURITY, EX_NULL_POINTER)创建。
  • AStatus_fromServiceSpecificError(error_code): 创建自定义业务错误码(比如 CarPlay 连接失败返回 404)。
  • AStatus_fromStatus(status): 将底层的 binder_status_t(如 STATUS_NO_MEMORY)转换为 AStatus。
  1. 检查状态
  • AStatus_isOk(const AStatus* status): 判断是否成功。
  • AStatus_getExceptionCode(const AStatus* status): 获取异常码。
  • AStatus_getServiceSpecificError(const AStatus* status): 获取你定义的业务错误码。
  • AStatus_getMessage(const AStatus* status): 获取错误详情字符串。
  1. 销毁状态
  • AStatus_delete(AStatus* status): 非常重要。所有通过 AStatus_new... 或 AStatus_from... 创建的裸指针,都必须手动调用此函数释放,否则会造成严重的内存泄漏。

AIBinder_DeathRecipient

一个死亡通知监听器。它并不属于 Binder 通信的主流数据通道,而是一个侧向的监控机制:当被监听的 Binder 句柄所在的进程意外终止(死亡)时,系统会回调你预设的函数。

  1. 创建监听器对象 AIBinder_DeathRecipient_new(AIBinder_DeathRecipient_onBinderDied onBinderDied), AIBinder_DeathRecipient_onBinderDied其原型为void onBinderDied(void* cookie)
  2. 绑定到 Binder 对象 AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie)
  3. 解除绑定 AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie)
  4. 销毁监听器 AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient)

注意

  • 生存期陷阱: AIBinder_DeathRecipient 对象本身必须比被监听的 Binder 活得久。如果你的 ScopedAIBinder_DeathRecipient 是函数内的局部变量,函数执行完它就析构了,死亡通知也就永远收不到了。
  • 线程上下文: onBinderDied 回调通常发生在一个专门的 Binder 线程中。如果回调里涉及修改 UI 或复杂的资源竞争,记得加锁(比如 std::mutex)。
  • Cookie 安全: 传入的 cookie(如 this)必须确保在回调发生时依然有效。如果你的服务对象已经析构了才收到死亡通知,就会发生 Use-after-free。

AParcel

Android 系统中用于跨进程传输(IPC)的数据容器

  • 数据打包: 支持基础类型(int, long, float)、字符串、数组。
  • 传输对象: 支持传输其他的 AIBinder 对象(实现嵌套通信)。
  • 传输资源: 支持传输文件描述符(File Descriptor)。 方法有:
  1. 写入数据:AParcel_writeXxx(AParcel* parce, Xxx data)
  2. 读取数据:AParcel_readXxx(AParcel* parcel, &Xxx data)
  3. 状态管理:
    • AParcel_delete/AParcel_create,手动创建删除;使用ScopedAParcel处理
    • AParcel_getDataSize: 获取当前 Parcel 中已存储的数据大小。
    • AParcel_setDataPosition: 移动读写指针。

Xxx数据类型

  • int32,int64,float,double
  • string,byteArray
  • parcelFileDescriptor:文件描述符
  • strongBinder:binder

ABinderProcess

管理binder线程池,在binder_process头文件内;有两种模式

  1. 标准线程池模式:有三个方法
  • AServiceManager_setThreadPoolMaxThreadCount(uint32_t maxThreads),设置 Binder 线程池的最大并发线程数。默认值15,一般选择4;有可能失败,必须在所有 Binder 行为发生之前调用
  • AServiceManager_startThreadPool(),非阻塞,启动binder线程池
  • AServiceManager_joinThreadPool(),阻塞当前主线程,加入线程池并进入无限循环等待。
  1. 单线程/集成模式:两个方法
  • ABinderProcess_setupPolling(int* fd):配置当前进程,使其支持"轮询"模式。它会返回一个文件描述符。就可以配合epoll、select 或者 Android 系统的 ALooper使用了
  • ABinderProcess_handlePolledCommands():真正执行处理命令。当检测到fd有数据可读时,告诉binder驱动读取数据并分发给对应的AIDL实现类处理

AServiceManager

与系统服务管家打交道,是服务注册与获取的核心。头文件 binder_manager中

  1. 服务注册类(服务端使用)
  • binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance)
  1. 服务获取类(客户端使用)
  • AIBinder* AServiceManager_getService(const char* instance)
  • AIBinder* AServiceManager_checkService(const char* instance)
  • AIBinder* AServiceManager_waitForService(const char* instance);
  1. 生命周期与枚举类(高级用途)
  • bool AServiceManager_isDeclared(const char* instance):在 VINTF(供应商接口对象)中检查服务是否已声明。这通常用于动态判断当前硬件平台是否支持某个功能。

  • AServiceManager_registerForServiceNotifications:注册一个监听器。当某个服务状态发生变化(比如重启了)时,你会收到通知。这对于中间件的断线重连逻辑至关重要。

如果在此文章中您有所收获,请给作者一个鼓励,点个赞,谢谢支持

技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给予关注和点赞;如果文章存在错误,也请多多指教!

相关推荐
小李子呢02112 小时前
前端八股Vue---自定义组件(控件)
前端·javascript·vue.js
用户52709648744902 小时前
微前端(qiankun)单侧启动调试技巧
前端
SamDeepThinking2 小时前
开篇词:6000万会员规模下,我们是怎么做秒杀系统的
java·后端·架构
历程里程碑2 小时前
二叉树---翻转二叉树
开发语言·c++·elasticsearch·链表·搜索引擎·tornado·dash
闻缺陷则喜何志丹2 小时前
【排序】P6149 [USACO20FEB] Triangles S|普及+
c++·算法·排序·洛谷
斌味代码2 小时前
jQuery 内存泄漏排查:常见场景、工具使用与修复实战
前端·javascript·jquery
GOWIN革文品牌咨询2 小时前
国际B2B品牌官网架构方法:如何把资料库重构成“认知中枢”
架构
weixin199701080162 小时前
《爱回收商品详情页前端性能优化实战》
前端·性能优化
tankeven2 小时前
HJ178 【模板】双指针
c++·算法