第三章 Android车机系统架构
好,咱们进入正题。这一章我打算聊聊Android车机系统的整体架构。说实话,很多刚入行的朋友一上来就盯着CAN总线代码看,结果越看越迷糊。为什么?因为你不了解整个系统是怎么搭起来的。
我个人的习惯是,先看地图再走路。Android车机系统说白了就是一个分层结构,每一层各司其职。咱们从下往上捋一遍。
3.1 Android Automotive OS概述
Android Automotive OS,注意不是Android Auto。这两个东西完全不一样。Android Auto是手机投屏方案,而Automotive OS是真正跑在车机硬件上的操作系统。
我记得2017年Google刚推出这个系统时,圈内很多人不看好。觉得Android手机都卡,跑车上能行吗?但几年下来,事实证明这套系统确实能打。它跟普通Android最大的区别在哪?我列几个关键点:
- 电源管理:车机不能像手机一样关机,得支持深度睡眠和快速唤醒
- 音频策略:导航、电话、媒体音要能独立控制,互不干扰
- 硬件抽象:CAN总线、GPS、车载传感器这些都得有专门的HAL层
- 安全机制:驾驶安全相关的应用优先级最高,不能随便被杀掉
你想想看,普通Android手机死机了重启就行。车机要是死机了,倒车影像出不来,那可不是闹着玩的。所以Automotive OS在稳定性上做了大量定制。
3.2 车机硬件抽象层(HAL)
HAL层,说白了就是硬件和系统之间的翻译官。我刚开始做车机项目时,最头疼的就是这层。为什么?因为每家车厂的硬件都不一样,但系统层代码又不能随便改。
Android Automotive的HAL主要包含这么几个模块:
| HAL模块 | 功能说明 | 我遇到的坑 |
|---|---|---|
| Vehicle HAL | 车辆属性接口,车速、车门、灯光等 | 属性ID定义各家不统一 |
| Audio HAL | 音频路由和策略控制 | 多音区混音容易出问题 |
| Camera HAL | 车载摄像头,特别是环视 | 延迟要求极高,丢帧是大忌 |
| Sensors HAL | 加速度计、陀螺仪等 | 数据融合算法要调参 |
这里重点说说Vehicle HAL。它是车机系统里最核心的HAL,负责跟CAN总线打交道。它的工作流程大概是这样的:
// Vehicle HAL的核心接口
IVehicle* vehicle = getVehicleService();
// 订阅车速信号
vehicle->subscribe(VehicleProperty::PERF_VEHICLE_SPEED);
// 回调处理
void onPropertyEvent(vehicle_prop_value_t* value) {
// 这里拿到的是经过HAL解析后的数据
float speed = value->value.floats[0];
// 嗯,单位是m/s,记得转成km/h
}
我曾经在一个项目里踩过坑。当时车厂提供的CAN协议里,车速信号是16位整型,但HAL层默认当浮点处理。结果车速显示忽快忽慢,查了两天才找到原因。所以做HAL开发时,一定要跟硬件团队确认好数据类型和精度。
3.3 车机服务架构
再往上走,就是服务层了。Android Automotive的服务架构跟普通Android不太一样。它多了几个车机专属的系统服务:
- CarService:车机服务的总入口,管理所有车辆功能
- CarPropertyService:车辆属性管理,读写车速、里程等
- CarAudioService:音频策略管理,控制不同音源的优先级
- CarNavigationService:导航服务,跟仪表盘交互
这些服务都跑在系统进程中,权限很高。应用层想获取车辆数据,得通过CarService来间接访问。这样做的好处是安全——普通应用不能直接操作硬件。
我建议你在做服务开发时,特别注意两点:
第一,服务启动顺序。 CarService必须在Vehicle HAL初始化完成后才能启动。否则一启动就崩溃,而且崩溃原因很难排查。
第二,线程模型。 车机服务里大量使用Binder通信,回调都是异步的。处理不当容易出并发问题。
3.4 车机应用框架
最上层就是应用层了。Android Automotive的应用框架跟普通Android基本一致,但多了几个车机专用的组件:
// 车机应用的基本结构
public class CarApp extends Activity {
private Car car;
private CarPropertyManager propertyManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 连接CarService
car = Car.createCar(this);
car.connect();
// 获取属性管理器
propertyManager = (CarPropertyManager)
car.getCarManager(Car.PROPERTY_SERVICE);
}
private void getVehicleSpeed() {
// 读取车速
CarPropertyValue<Float> speed =
propertyManager.getProperty(
VehicleProperty.PERF_VEHICLE_SPEED, 0);
if (speed != null) {
float currentSpeed = speed.getValue();
// 更新UI
}
}
}
这里有个重要的设计原则:应用层不能直接调用HAL接口。所有车辆数据的读写,都必须通过CarService中转。这样做是为了保证系统安全性和稳定性。
小技巧: 调试车机应用时,可以用adb命令模拟车辆信号:
# 模拟车速100km/h
adb shell dumpsys car_service inject-property \
--prop 291 --value 27.78
注意单位是m/s,100km/h约等于27.78m/s。
说到应用框架,我还想提一下UI设计。车机应用的UI跟手机完全不同。驾驶过程中,驾驶员注意力有限,所以Google搞了一套模板化的UI组件:
- 列表项要够大,方便触控
- 文字要简洁,一眼能看懂
- 动画要少,避免分散注意力
- 关键信息要突出,比如导航提示
警告: 千万不要在车机应用里做复杂的动画或者频繁刷新UI。我曾经见过一个第三方音乐App,播放时封面图转个不停,结果导致系统UI线程卡顿,导航语音都延迟了。这种问题在车机上是非常危险的。
好了,这一章的内容就这些。Android车机系统架构说白了就是四层:操作系统、硬件抽象、系统服务、应用框架。每一层各司其职,层与层之间通过标准接口通信。理解了这些,后面讲CAN总线通信时你就能知道数据是怎么从物理总线一路传到应用界面的。
下一章咱们开始聊CAN总线的基础知识,到时候会涉及一些硬件层面的东西,做好准备。