4、HIDL与AIDL基础:HIDL接口定义语言、AIDL与HIDL的区别、在MTK8676上创建第一个HIDL服务
好,我们进入第四章。这一章要聊的是HIDL和AIDL。
说实话,我刚接触Android HAL层开发那会儿,也被这两个东西搞晕过。它们都是接口定义语言,长得也像,但用途完全不同。在MTK8676这个平台上,你经常会同时遇到它们。搞不清楚,后面写代码就会踩坑。
今天我就带你彻底理清楚。咱们从HIDL开始讲起。
4.1 HIDL接口定义语言
HIDL,全称是HAL Interface Definition Language。它是Android 8.0之后引入的。目的是把HAL模块和Android框架彻底解耦。
你想想看,以前的HAL是直接编译进system_server进程的。一个HAL崩溃,整个系统就挂了。HIDL就是为了解决这个问题。它让HAL跑在独立的进程中,通过IPC通信。
核心思想:HIDL定义的是服务端和客户端之间的契约。服务端实现接口,客户端调用接口。两者通过Binder通信。
在MTK8676上,HIDL文件通常放在 hardware/interfaces/ 目录下。它的后缀是 .hal。语法上,它有点像C++和Java的混合体。
看一个简单的例子:
package vendor.mediatek.hardware.example@1.0;
interface IExample {
hello(string name) generates (string result);
getVersion() generates (int32_t major, int32_t minor);
};
这里我解释一下:
package定义了包名,类似Java的命名空间。MTK的包名通常以vendor.mediatek开头。@1.0是版本号。HIDL非常强调版本管理。接口一旦发布,就不能改。只能通过增加新版本兼容。interface定义接口名。方法后面用generates关键字指定返回值。- 注意,HIDL的方法可以返回多个值。比如
getVersion返回了两个int32。
个人经验:我建议你在定义HIDL接口时,尽量把方法拆细。一个方法只做一件事。别搞那种又长又复杂的接口。我在MTK项目上见过一个接口有20多个方法,维护起来简直噩梦。
4.2 AIDL与HIDL的区别
好,讲完HIDL,咱们说说AIDL。AIDL是Android Interface Definition Language。它比HIDL出现得早。Android系统内部大量使用AIDL。
它们到底有什么区别?我直接给你一张表,一目了然:
| 对比项 | HIDL | AIDL |
|---|---|---|
| 引入时间 | Android 8.0 | Android 1.0 |
| 主要用途 | HAL层接口定义 | 应用层与系统服务通信 |
| 语言 | 独立.hal文件 | .aidl文件(Java风格) |
| 版本管理 | 强制版本号,接口不可变 | 无强制版本管理 |
| 进程模型 | HAL运行在独立进程 | 服务运行在system_server |
| 稳定性 | 高,接口冻结后不可改 | 相对灵活,可随时修改 |
| 典型场景 | 传感器、摄像头、音频HAL | ActivityManager、WindowManager |
说白了,HIDL是给硬件厂商用的。AIDL是给应用框架用的。在MTK8676上,你写HAL层代码,主要用HIDL。但有时候也会用到AIDL,比如和系统服务交互。
避坑指南:我曾经在MTK平台上犯过一个错误。我把一个HIDL接口定义成了AIDL风格,结果编译死活过不去。记住,HIDL的语法和AIDL不一样。HIDL用 generates 而不是 return。HIDL不支持Java的泛型。这些细节不注意,编译报错会让你找半天。
4.3 在MTK8676上创建第一个HIDL服务
理论讲完了,咱们动手。在MTK8676上创建一个HIDL服务,其实不复杂。我带你走一遍完整流程。
第一步:定义HIDL接口
在 hardware/interfaces/ 下创建目录结构:
mkdir -p vendor/mediatek/hardware/hello/1.0/default
然后创建 IHello.hal 文件:
package vendor.mediatek.hardware.hello@1.0;
interface IHello {
sayHello(string name) generates (string message);
};
这个接口很简单。就一个方法,传入名字,返回问候语。
第二步:生成代码
MTK的编译系统会自动处理HIDL文件的代码生成。你只需要在 Android.bp 中声明:
hidl_interface {
name: "vendor.mediatek.hardware.hello@1.0",
root: "vendor.mediatek.hardware",
srcs: ["IHello.hal"],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: true,
gen_java_constants: true,
};
运行 make hidl-gen 或者全编译,系统会自动生成C++和Java的桩代码。
第三步:实现服务端
在 default/ 目录下创建 Hello.cpp:
#include <vendor/mediatek/hardware/hello/1.0/IHello.h>
#include <hidl/LegacySupport.h>
using vendor::mediatek::hardware::hello::V1_0::IHello;
using android::hardware::Return;
using android::hardware::Void;
struct HelloImpl : public IHello {
Return<android::String16> sayHello(const android::String16& name) override {
std::string msg = "Hello, " + std::string(name.c_str()) + "!";
return android::String16(msg.c_str());
}
};
int main() {
return android::hardware::defaultPassthroughServiceImplementation<HelloImpl>("hello");
}
这里要注意,HIDL服务默认是passthrough模式。它会直接加载到调用进程。如果你想让它跑在独立进程,需要注册为binderized服务。
第四步:注册服务
在 Android.bp 中添加可执行文件编译规则:
cc_binary {
name: "android.hardware.hello@1.0-service",
relative_install_path: "hw",
srcs: ["Hello.cpp"],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.mediatek.hardware.hello@1.0",
],
init_rc: "android.hardware.hello@1.0-service.rc",
};
同时创建 .rc 文件,让服务在系统启动时自动运行:
service hello-hal /vendor/bin/hw/android.hardware.hello@1.0-service
class hal
user system
group system
第五步:客户端调用
在任意进程中,你可以这样调用:
using vendor::mediatek::hardware::hello::V1_0::IHello;
sp<IHello> service = IHello::getService();
if (service != nullptr) {
service->sayHello("World", [](const android::String16& result) {
ALOGD("Result: %s", std::string(result.c_str()).c_str());
});
}
个人习惯:我每次写完HIDL服务,都会先写一个简单的客户端测试程序。确保服务能正常启动和响应。别等到集成到系统里才发现问题。那时候排查起来就麻烦了。
嗯,到这里,你已经掌握了在MTK8676上创建HIDL服务的基本流程。从接口定义到代码生成,再到服务实现和注册,每一步我都带你走了一遍。
下一章,我们会深入HIDL的进阶用法。包括回调、同步异步、以及性能优化。这些在实际项目中非常关键。