30、综合项目实战:需求分析、架构设计、功能实现、测试与发布、项目总结
终于到了最后一章。说实话,走到这一步不容易。
前面29章,我们拆解了Android Automotive OS的方方面面。从系统架构到HAL层,从车载应用到OTA升级。但光有知识点还不够,你得把它们串起来。
这一章,我们就拿一个真实项目练手。我管它叫「车载智能助手」——一个集语音控制、车辆状态监测、导航联动于一体的车载应用。嗯,这活儿我一个人干过,踩了不少坑,今天全抖出来给你看。
1. 需求分析:别急着写代码
我见过太多人,拿到需求就开干。结果呢?做到一半发现方向错了。
需求分析,说白了就是搞清楚「做什么」和「为什么做」。对于车载项目,这一点尤其重要。因为车机环境比手机复杂得多——安全、稳定、低延迟,缺一不可。
核心需求清单(MVP版本)
- 语音唤醒与指令识别:支持「你好,小智」唤醒,识别导航、音乐、空调控制指令
- 车辆状态显示:实时展示车速、电量、续航里程、胎压
- 导航联动:接收语音目的地,调用系统导航,并在主界面显示导航信息
- 多屏适配:支持中控屏(1920x720)和仪表盘(1280x480)
- 驾驶安全约束:车辆行驶时,限制视频播放和复杂操作
你可能会问:「需求就这么点?」
对,MVP就这么多。我个人的习惯是:先做核心闭环,再慢慢加功能。车载项目尤其如此——功能越多,测试越复杂,出问题的概率越大。
避坑指南
我曾经在一个项目里,一口气加了10个功能。结果呢?光联调就花了三周。后来我学乖了:MVP只做3-5个核心功能,跑通流程再迭代。
2. 架构设计:分层解耦,各司其职
需求明确了,接下来就是架构设计。车载应用架构,我推荐分层设计。为什么?因为好维护、好测试、好扩展。
下面这张图,是我自己画的项目架构图。你看一眼就明白了。
你看,从上到下,职责非常清晰:
- UI层:只负责展示和用户交互。我用的是Jetpack Compose + Car UI Library,适配多屏很方便。
- ViewModel层:管理UI状态,编排业务逻辑。说白了,就是UI和Service之间的「翻译官」。
- Service层:封装系统API,处理后台任务。比如语音识别、车辆数据采集。
- HAL/系统层:这是Android Automotive OS的底层,直接和硬件打交道。我们一般不动它,只调用标准接口。
注意:分层架构最大的好处是「可替换」。比如你想把语音识别从百度换成讯飞,只需要改Service层,UI层完全不用动。我在项目中吃过耦合的亏,后来强制分层,维护成本降了一半。
3. 功能实现:核心代码走一遍
架构搭好了,咱们开始写代码。我挑三个核心功能讲讲。
3.1 车辆状态实时获取
车辆状态数据,通过Vehicle HAL提供。我们只需要订阅对应的Property ID。
// 订阅车速和电量
val speedProperty = VehiclePropertyIds.PERF_VEHICLE_SPEED
val batteryProperty = VehiclePropertyIds.EV_BATTERY_LEVEL
// 在VehicleService中订阅
car.connectCarService()
car.getVehicleManager()?.let { manager ->
manager.addCallback(speedProperty, 0) { value ->
// 更新车速到ViewModel
_speedLiveData.postValue(value.floatValue)
}
manager.addCallback(batteryProperty, 0) { value ->
_batteryLiveData.postValue(value.floatValue)
}
}
这里有个坑:Vehicle HAL的回调频率很高。如果你直接更新UI,性能会崩。我建议在ViewModel里做节流处理,比如每200ms更新一次。
3.2 语音唤醒与指令解析
语音唤醒,我用的是离线唤醒词方案。这样不依赖网络,响应更快。
// 初始化语音唤醒
val wakeupEngine = WakeupEngine.create(context)
wakeupEngine.setWakeupWord("你好小智")
wakeupEngine.setWakeupCallback {
// 唤醒成功,启动语音识别
startVoiceRecognition()
}
// 语音识别结果解析
fun parseVoiceCommand(text: String): VoiceCommand {
return when {
text.contains("导航到") -> {
val destination = text.substringAfter("导航到")
VoiceCommand.Navigate(destination)
}
text.contains("空调") && text.contains("开") -> VoiceCommand.ACOn
text.contains("空调") && text.contains("关") -> VoiceCommand.ACOff
text.contains("播放音乐") -> VoiceCommand.PlayMusic
else -> VoiceCommand.Unknown
}
}
你想想看,如果用户说「导航到天安门」,你的解析器能正确提取目的地吗?我建议用正则或者简单的关键词匹配,别一上来就上NLP,车载场景指令有限,够用就行。
3.3 驾驶安全约束
这个功能,说白了就是「车动了,有些操作就不让做了」。Android Automotive OS提供了CarOccupantZoneManager来判断驾驶位。
// 判断当前是否在驾驶位
val occupantZoneManager = car.getCarManager(CarOccupantZoneManager::class.java)
val isDriver = occupantZoneManager?.let {
it.getOccupantZone(OccupantZoneInfo.ROLE_DRIVER)?.isOccupant
} ?: false
// 获取车辆运动状态
val vehicleSpeed = _speedLiveData.value ?: 0f
if (vehicleSpeed > 0.5f && isDriver) {
// 限制视频播放、复杂操作
showBlockingUI()
}
个人经验:车速阈值别设太低。我一开始设了0.1m/s,结果等红灯时稍微溜车就触发限制了,用户疯狂投诉。后来改成0.5m/s(约1.8km/h),体验好多了。
4. 测试与发布:别让bug上路
车载应用的测试,比手机应用严格得多。因为bug可能导致安全事故。
我一般分三步走:
- 单元测试:用JUnit + Mockito,测试ViewModel和Service层的逻辑。比如语音指令解析、车速阈值判断。
- 集成测试:用Android Automotive模拟器,模拟车辆状态变化。测试多屏交互、语音唤醒等场景。
- 实车测试:这是最关键的。找一台开发板或者实车,跑一遍所有功能。我建议至少跑100公里,覆盖城市、高速、隧道等场景。
测试清单(部分)
| 测试项 | 测试场景 | 预期结果 |
|---|---|---|
| 语音唤醒 | 车速80km/h,开窗,播放音乐 | 唤醒成功率 > 90% |
| 导航联动 | 语音输入「导航到北京西站」 | 正确解析并启动导航 |
| 安全约束 | 车速 > 5km/h,点击视频播放 | 弹出提示,禁止播放 |
| 多屏适配 | 中控屏和仪表盘同时显示 | 布局正确,无重叠 |
发布时,我建议走Google Play for Automotive。它会自动做兼容性检查。不过要注意,车载应用更新频率别太高,用户不会像手机那样频繁升级。
5. 项目总结:复盘一下
项目做完了,咱们复盘一下。说实话,这个「车载智能助手」项目,麻雀虽小五脏俱全。
你学到了什么?
- 需求分析:别贪多,MVP先跑通。
- 架构设计:分层解耦,好维护好扩展。
- 功能实现:Vehicle HAL、语音唤醒、安全约束,都是车载核心能力。
- 测试发布:实车测试不能省,安全第一。
我记得第一次做车载项目时,光Vehicle HAL的权限问题就折腾了两天。后来发现是manifest里少声明了一个权限。嗯,这种坑踩一次就够了。
最后送你一句话:车载开发,慢就是快。多花时间在设计和测试上,上线后你会感谢自己的。
如果你对某个模块有疑问,或者想深入聊聊,欢迎来找我。公众号:蓝海资料掘金营,微信deep3321