23、HIDL与AIDL迁移:从HIDL到AIDL的接口迁移策略,稳定性与性能权衡

各位Android系统开发的同行们,今天我们来聊聊HIDL到AIDL的迁移。这个话题,说白了就是Google在Android系统接口定义上的一次大洗牌。我最早接触HIDL还是在Android 8.0刚推出Treble架构的时候,那时候觉得这玩意儿真不错,把framework和hal层彻底解耦了。但到了Android 11、12,Google又开始推AIDL作为统一接口语言。嗯,这里面的门道,我们得好好掰扯掰扯。

为什么要有这次迁移?

先说说背景。HIDL(HAL Interface Definition Language)是Treble架构的产物,专门用来定义framework和hal之间的接口。它解决了之前直接加载so库导致的各种兼容性问题。但问题来了——Android系统里其实有两套接口语言:HIDL给hal用,AIDL给应用层用。你想想看,维护两套东西,学习成本、工具链、代码生成器都得双份,这显然不划算。

我个人习惯把这次迁移理解为「统一语言运动」。Google想把所有跨进程通信都统一到AIDL上,这样开发者只需要学一套东西。而且AIDL在Android生态里已经打磨了十几年,稳定性、工具链成熟度都比HIDL强不少。

核心结论:从HIDL到AIDL迁移,本质上是Android系统接口定义的「大一统」过程。不是为了炫技,而是为了降低维护成本、统一开发体验。

迁移策略:三步走

我在项目中实际做过几次迁移,总结下来就是三步:接口转换、实现适配、测试验证。别想着一步到位,那容易翻车。

第一步:接口定义转换

HIDL的接口定义文件是.hal,AIDL的是.aidl。语法上有差异,但逻辑结构是相通的。举个例子:

// HIDL 写法
interface IMySensor {
    getData() generates (int32_t value, string error);
};

// AIDL 写法
interface IMySensor {
    int getData() throws RemoteException;
};

你看,HIDL用generates关键字表示返回值,AIDL直接就是方法签名。这里有个坑——HIDL支持多个返回值,AIDL只支持一个。我在迁移一个传感器hal时,就遇到了返回三个值的接口。怎么办?封装成一个Parcelable对象呗。

小技巧:HIDL的callback机制在AIDL里用oneway关键字实现。记得加上@VintfStability注解,这是AIDL支持vendor接口的关键。

第二步:实现层适配

接口定义改完了,接下来是服务端和客户端的实现。HIDL的服务端继承的是android::hardware::V1_0::IMySensor,AIDL继承的是IMySensor.Stub。这里有个重要的变化——HIDL的binder对象是hwservice,AIDL是标准的servicemanager

我曾经在迁移一个音频hal时,发现HIDL的服务注册方式跟AIDL完全不同。HIDL用registerAsService,AIDL用addService。而且HIDL的服务名是带版本号的,比如android.hardware.audio@2.0::IDevicesFactory,AIDL直接就是android.hardware.audio.IDevicesFactory。这个命名变化,如果你不注意,服务注册失败都不知道怎么回事。

第三步:测试与验证

迁移完一定要做兼容性测试。我建议用VTS(Vendor Test Suite)跑一遍,特别是那些跟接口定义相关的测试用例。另外,别忘了做性能对比——同样的接口调用,AIDL和HIDL在延迟、吞吐量上有没有差异?

稳定性与性能权衡

说到性能,这是大家最关心的。我直接说结论:AIDL在大多数场景下性能不输HIDL,甚至更好。但有几个细节要注意:

对比维度 HIDL AIDL
序列化开销 使用hidl-gen生成,较复杂 使用aidl-gen生成,更轻量
Binder传输 走hwservicemanager 走servicemanager,路径更短
内存拷贝 有额外内存拷贝 零拷贝优化更好
稳定性 版本号严格,兼容性好 通过@VintfStability保证

你看这个表格,AIDL在性能上其实有优势。但稳定性方面,HIDL的版本号机制确实更严格。AIDL的@VintfStability注解虽然也能保证接口稳定,但需要开发者主动维护。我建议在迁移时,把接口版本号写进注释里,方便后续排查。

避坑指南:我曾经在迁移一个摄像头hal时,发现AIDL的oneway调用在低端设备上会出现丢消息的情况。后来排查发现是binder线程池不够用。解决办法是调大ProcessState::self()->setThreadPoolMaxThreadCount()的值。嗯,这个坑踩得值。

迁移中的常见问题

我整理了几个高频问题,都是我在项目中实际遇到过的:

  • 接口版本兼容:HIDL支持@1.0@2.0这样的版本号,AIDL没有。怎么办?用@VintfStability@Version注解模拟版本控制。
  • 回调接口:HIDL的hidl_callback在AIDL里用oneway interface实现。注意回调接口也要加@VintfStability
  • 内存管理:HIDL的hidl_memory在AIDL里用SharedMemory替代。共享内存的fd传递方式不同,记得做适配。
  • 服务发现:HIDL用getService,AIDL用getServicewaitForService。后者支持超时设置,更灵活。

知识体系总览

为了让大家更直观地理解整个迁移过程,我画了一张图:

HIDL到AIDL迁移知识体系 HIDL 阶段 • .hal 接口定义 • hidl-gen 代码生成 • hwservicemanager • 版本号 @1.0/@2.0 • hidl_memory • 多返回值支持 • callback 机制 • 仅用于 vendor 层 迁移策略 ① 接口转换 .hal → .aidl ② 实现适配 服务端/客户端重写 ③ 测试验证 VTS + 性能对比 ④ 兼容性处理 @VintfStability AIDL 阶段 • .aidl 接口定义 • aidl-gen 代码生成 • servicemanager • @VintfStability • SharedMemory • 单返回值+Parcelable • oneway 回调 • 统一 framework/vendor 核心目标:统一接口语言,降低维护成本,提升开发效率

这张图把整个迁移过程分成了三个阶段。左边是HIDL的特性,右边是AIDL的特性,中间是迁移策略。你对照着看,就知道每一步该做什么了。

性能权衡的实战经验

说到性能,我分享一个真实案例。之前迁移一个传感器hal,HIDL版本每次调用延迟大约200微秒。迁移到AIDL后,延迟降到了150微秒左右。为什么?因为AIDL的binder传输路径更短,而且序列化更高效。但要注意,如果你的接口调用频率极高(比如每帧都要调用),那AIDL的线程模型可能会成为瓶颈。这时候可以考虑用oneway调用,或者把多个调用合并成一个批量接口。

另一个要注意的是内存开销。HIDL的hidl_memory在传递大块数据时,会有额外的内存拷贝。AIDL的SharedMemory通过文件描述符传递,避免了拷贝。我在迁移一个图像处理hal时,内存占用直接降了30%。

我的建议:如果你的接口调用频率低于1000次/秒,直接迁移AIDL,性能只会更好。如果频率更高,建议做压力测试,必要时用oneway或批量接口优化。

总结

HIDL到AIDL的迁移,说白了就是一次接口语言的统一。Google推这个,不是为了折腾开发者,而是为了让Android系统更简洁、更高效。迁移过程中,接口转换、实现适配、测试验证这三步走稳了,基本不会出大问题。性能方面,AIDL在大多数场景下优于HIDL,但高频调用场景需要额外关注线程模型和内存管理。

最后说一句,迁移不是目的,兼容性和稳定性才是。别为了赶时髦而迁移,如果你的HIDL接口运行得好好的,而且短期内没有新功能需求,完全可以等下一个大版本再动。但如果你要开发新功能,或者要支持Android 12以上的设备,那还是尽早迁移到AIDL吧。


公众号:蓝海资料掘金营,微信deep3321