10、车载导航系统:CarNavigationService 接口、导航状态上报、地图应用集成要点
导航系统,可以说是车载智能座舱里最核心的应用之一。用户坐进车里,第一件事往往就是设置目的地。我这些年做车载系统,光是导航相关的坑就踩了不下十几个。今天咱们就聊聊 CarNavigationService 这个系统服务,看看它到底怎么跟地图应用打交道。
10.1 CarNavigationService 是什么?
说白了,CarNavigationService 是 Android Automotive 系统提供给上层地图应用的一个标准接口。它定义了一套规范,告诉地图应用:「你该怎么跟我汇报导航状态,我该怎么把你的信息展示给用户。」
我个人习惯把 CarNavigationService 理解成一个「翻译官」。地图应用说自己的语言,系统 UI 说系统的语言,CarNavigationService 就在中间做转换。没有它,每个地图应用都得自己跟系统 UI 对接,那可就乱套了。
核心作用:CarNavigationService 负责接收地图应用的导航状态,然后把这些状态分发给系统 UI 和其他车载服务。它定义在 packages/services/Car/service 目录下,属于 CarService 的一部分。
10.2 导航状态上报机制
地图应用怎么告诉系统「我现在在导航」?答案是通过 CarNavigationManager。应用端拿到 CarNavigationManager 的实例后,就可以调用它的方法来上报状态了。
我记得第一次对接导航状态时,有个细节让我折腾了两天——状态上报的时机。你想想看,如果应用在导航开始前就上报了「导航中」,系统 UI 会提前显示导航信息,但此时地图可能还没准备好路线。反过来,如果上报晚了,用户都开出去两公里了,仪表盘上还没显示导航信息。
所以,正确的做法是:
- 导航准备就绪时:调用
onNavigationStarted() - 导航进行中:持续调用
onNavigationStateChanged()更新状态 - 导航结束时:调用
onNavigationEnded()
来看一个简单的代码示例:
// 地图应用端上报导航状态
CarNavigationManager navManager =
(CarNavigationManager) car.getCarManager(Car.CAR_NAVIGATION_SERVICE);
// 导航开始
navManager.onNavigationStarted();
// 更新导航状态
NavigationState state = new NavigationState.Builder()
.setDestinationName("北京南站")
.setRemainingDistance(15000) // 单位:米
.setRemainingTime(1200) // 单位:秒
.setCurrentRoadName("京沪高速")
.build();
navManager.onNavigationStateChanged(state);
// 导航结束
navManager.onNavigationEnded();
小提示:NavigationState 这个类里有很多字段,但不是每个都必须填。我个人建议至少填上目的地名称、剩余距离和剩余时间,这三个是用户最关心的。
10.3 地图应用集成要点
地图应用要集成到 Automotive 系统里,有几个关键点需要注意。我当年踩过的坑,今天一次性告诉你。
10.3.1 权限声明
地图应用必须声明 android.car.permission.CAR_NAVIGATION_MANAGER 权限。这个权限是 signature 级别的,意味着只有系统应用或者有系统签名的应用才能使用。
<uses-permission android:name="android.car.permission.CAR_NAVIGATION_MANAGER" />
嗯,这里要注意。如果你的地图应用是第三方预装的,记得让 OEM 在系统签名时把这个权限加上。否则应用根本拿不到 CarNavigationManager 的实例。
10.3.2 生命周期管理
地图应用的生命周期跟普通手机应用不太一样。在车上,地图应用可能长时间运行,也可能被其他应用抢占焦点。我曾经遇到过一个 case:地图应用被切到后台后,导航状态没有及时更新,导致仪表盘上一直显示旧数据。
解决方案是:在 onPause() 和 onResume() 里重新检查导航状态,确保状态上报跟应用的生命周期同步。
@Override
protected void onResume() {
super.onResume();
if (isNavigating) {
navManager.onNavigationStateChanged(currentState);
}
}
@Override
protected void onPause() {
super.onPause();
// 不要在这里停止导航,只是暂停状态更新
// 导航应该继续在后台运行
}
10.3.3 多屏交互
现在的车载系统通常有多个屏幕:中控屏、仪表盘、HUD。地图应用需要把导航信息同步到所有屏幕上。CarNavigationService 的设计初衷就是为了解决这个问题——应用只需要上报一次状态,系统会自动分发到各个屏幕。
但这里有个坑:不同屏幕的刷新率不一样。仪表盘可能只需要每秒更新一次,而中控屏需要 60fps 的流畅度。所以,地图应用不要频繁调用 onNavigationStateChanged(),我建议控制在每秒 2-3 次以内,否则系统端处理不过来。
警告:千万不要在 UI 线程里频繁调用 CarNavigationManager 的方法。我曾经见过一个应用,每 100 毫秒就更新一次导航状态,结果导致系统 UI 卡顿。建议使用 Handler 或者协程来做节流。
10.4 系统架构图
下面这张图展示了 CarNavigationService 在整个车载系统中的位置,以及数据流向。我画这张图的时候特意简化了一些细节,方便你理解核心逻辑。
10.5 常见问题与避坑指南
做导航集成的过程中,有几个问题特别容易遇到。我根据自己的经验,整理了一份清单:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 导航状态不更新 | 应用没有正确绑定 CarService | 检查 Car.createCar() 是否成功,确认权限已授予 |
| 仪表盘显示延迟 | 状态上报频率太低 | 确保每秒至少上报一次关键状态 |
| 导航结束后 UI 未清除 | 没有调用 onNavigationEnded() | 在导航结束的 callback 里务必调用该方法 |
| 多屏显示不一致 | 不同屏幕的刷新机制不同 | 统一使用 CarNavigationService 分发,不要各自维护状态 |
曾经踩过的坑:有一次,地图应用在导航结束后没有调用 onNavigationEnded(),结果系统 UI 一直显示「您已到达目的地附近」,用户都开出去五公里了还在显示。排查了半天才发现是应用在退出导航页面时直接 finish() 了 Activity,根本没走导航结束的逻辑。所以,一定要在应用退出前确保状态清理干净。
10.6 性能优化建议
导航状态上报虽然看起来简单,但性能问题不容忽视。我建议你注意以下几点:
- 批量更新:如果多个状态同时变化(比如距离和时间),合并成一次调用,不要分开调用。
- 避免高频更新:每秒 2-3 次足够,太频繁反而增加系统负担。
- 使用异步线程:CarNavigationManager 的方法调用尽量放在子线程,避免阻塞 UI。
- 内存优化:NavigationState 对象不要频繁创建,可以复用同一个 Builder。
嗯,关于 CarNavigationService 的内容就讲到这里。导航集成看起来简单,但细节很多。你只要把状态上报的时机把握好,生命周期管理好,基本上就不会出大问题。