26、车载Android系统:CarService、车载HAL、多屏交互
车载Android,说白了就是把手机上的Android搬到了车上。但你别以为只是换个屏幕那么简单。车上的环境比手机复杂得多——空调、车窗、方向盘按键、仪表盘、中控屏、副驾屏……这些东西怎么协同工作?靠的就是今天要讲的这套体系。
我个人习惯把车载Android系统拆成三层来看:应用层(CarService)、硬件抽象层(车载HAL)、显示层(多屏交互)。这三层搞明白了,车载开发的基本盘就稳了。
核心观点:车载Android不是魔改Android,而是在标准Android框架上,叠加了一套汽车专属的服务和硬件抽象层。CarService是大脑,车载HAL是手脚,多屏交互是眼睛和耳朵。
一、CarService:车载应用的总管家
CarService是什么?它是Android Automotive OS里最核心的系统服务。你可以把它理解成一个专门管车的“系统级App”。它跑在system_server进程里,负责跟车相关的所有功能。
我记得第一次接触CarService时,翻代码翻得头大。后来发现,其实它主要干三件事:
- 车辆属性管理——车速、车门状态、空调温度、油量……这些数据都通过CarService统一暴露给上层App。
- 车辆功能控制——开空调、调音量、切换驾驶模式……App调用CarService的API,CarService再往下走。
- 多用户与安全策略——驾驶员在开车时,副驾能不能看视频?CarService里有一套策略来控制。
来看一个最简单的例子:获取当前车速。
// 获取CarService实例
Car car = Car.createCar(context);
if (car == null) {
// 车载环境才可用,模拟器里跑不了
return;
}
// 获取车辆属性管理器
CarPropertyManager propertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
// 读取车速(单位:km/h)
float speed = propertyManager.getFloatProperty(
VehiclePropertyIds.PERF_VEHICLE_SPEED, 0);
Log.d("CarDemo", "当前车速:" + speed + " km/h");
你想想看,如果没有CarService,每个App都得自己去跟硬件打交道,那不乱套了?CarService就是中间人,把底层硬件抽象成统一的API。
避坑指南:我曾经在项目里直接在主线程调CarPropertyManager的get方法,结果UI卡成PPT。记住,CarService的某些属性读取是跨进程调用,耗时不确定,一定要放到子线程或者用回调方式。
二、车载HAL:连接硬件与系统的桥梁
CarService再牛,它也不能直接拧螺丝。真正跟硬件打交道的是车载HAL(Hardware Abstraction Layer)。
车载HAL和手机上的HAL不太一样。手机HAL管的是摄像头、传感器、音频这些。车载HAL管的是:
- Vehicle HAL —— 车辆信号(车速、转向、灯光等)
- Audio HAL —— 车载音响、麦克风阵列
- Display HAL —— 多屏显示(中控、仪表、HUD)
- Input HAL —— 方向盘按键、旋钮、触摸板
其中最重要的就是Vehicle HAL。它定义了一套标准的车辆属性ID(VehiclePropertyIds),CarService通过这套ID来读写车辆数据。
来看一个Vehicle HAL的典型实现片段:
// Vehicle HAL 中的属性订阅示例(C++)
Return<StatusCode> VehicleHal::subscribe(
int32_t propertyId,
int32_t areaId,
float sampleRate) {
// 检查属性是否支持
if (!isPropertySupported(propertyId)) {
return StatusCode::INVALID_ARG;
}
// 注册回调,当属性值变化时通知CarService
mPropertyMap[propertyId].subscribers.push_back({
.areaId = areaId,
.sampleRate = sampleRate,
.callback = [this](VehiclePropValue& value) {
// 通过hidl/aidl回调到CarService
mCallback->onPropertyEvent({value});
}
});
return StatusCode::OK;
}
嗯,这里要注意:车载HAL是C++实现的,跑在独立的进程里。CarService通过AIDL/HIDL跟它通信。所以每次读写车辆属性,都是一次跨进程调用。性能敏感的场景(比如车速显示),一定要考虑延迟。
警告:千万不要在车载HAL里做耗时操作!我曾经见过一个供应商在HAL里加了网络请求来获取云端数据,结果导致车辆启动时CarService卡死30秒。HAL只做硬件读写,其他逻辑交给上层。
三、多屏交互:中控、仪表、副驾屏的协同
车载Android最头疼的问题之一就是多屏。手机只有一个屏,车上有三四个屏。而且每个屏的角色不一样:
| 屏幕 | 典型位置 | 主要用途 | 交互方式 |
|---|---|---|---|
| 中控屏 | 中央 | 导航、音乐、设置 | 触摸、语音 |
| 仪表屏 | 方向盘后 | 车速、续航、警示 | 方向盘按键 |
| 副驾屏 | 副驾前方 | 影音、游戏 | 触摸 |
| HUD | 前挡风 | 导航箭头、车速 | 无交互 |
多屏交互的核心问题有三个:
- 窗口管理——哪个App显示在哪个屏上?
- 焦点管理——方向盘按键控制哪个屏?
- 跨屏拖拽——导航能不能从中控拖到仪表?
Android原生的窗口管理(WindowManager)只支持单屏。车载上用的是CarWindowManager,它扩展了原生的窗口管理,支持多Display。
来看一个多屏显示的配置示例:
// 在CarService中注册多屏Display
CarDisplayManager displayManager =
(CarDisplayManager) car.getCarManager(Car.DISPLAY_SERVICE);
// 获取仪表屏的Display ID
int clusterDisplayId = displayManager.getDisplayIdForCluster();
// 将导航App显示到仪表屏
Intent intent = new Intent(context, NavigationActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(clusterDisplayId);
context.startActivity(intent, options.toBundle());
我个人习惯把多屏交互的架构画成下面这张图,方便理解:
从这张图可以看得很清楚:CarService在上层管业务逻辑,车载HAL在下层管硬件,中间通过系统服务层做桥接。多屏交互则贯穿整个架构,每个层级都要考虑。
四、实战中的几个坑
讲完理论,说几个我在项目中踩过的坑,希望对你有帮助。
坑1:CarService的启动顺序
我曾经在项目里遇到一个问题:车辆启动后,中控屏黑屏30秒才亮。查了半天,发现是CarService在等Vehicle HAL初始化。而Vehicle HAL又在等CAN总线唤醒。解决方案是把CarService的启动改成懒加载——先显示开机动画,等底层就绪了再加载车辆功能。
坑2:多屏的触摸事件冲突
有一次测试发现,副驾屏滑动时,中控屏的导航也跟着动。原因是两个屏的触摸事件被同一个InputDispatcher处理了。解决方案是在Display HAL里给每个屏分配独立的InputChannel,并在CarService里做焦点隔离。
坑3:车辆属性订阅的频控
车速显示要求每秒刷新30次,但Vehicle HAL的CAN总线读取上限是10Hz。如果CarService订阅频率超过硬件能力,会导致数据堆积和延迟。我当时的做法是在CarService里加一个频控层,把高频请求合并成低频轮询。
个人建议:刚开始做车载开发时,别急着写代码。先花一周时间把CarService的源码框架跑一遍,理解它的启动流程、属性订阅机制、多屏窗口管理。这些基础打牢了,后面遇到问题才能快速定位。
车载Android开发,说白了就是跟硬件打交道。CarService、车载HAL、多屏交互这三块,是绕不开的核心。你想想看,车上的每一个功能——从按一下空调按钮到仪表盘显示导航箭头——背后都是这三层在协同工作。搞懂了它们,你就掌握了车载Android的命脉。
公众号:蓝海资料掘金营,微信deep3321