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 会提前显示导航信息,但此时地图可能还没准备好路线。反过来,如果上报晚了,用户都开出去两公里了,仪表盘上还没显示导航信息。

所以,正确的做法是:

  1. 导航准备就绪时:调用 onNavigationStarted()
  2. 导航进行中:持续调用 onNavigationStateChanged() 更新状态
  3. 导航结束时:调用 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 在整个车载系统中的位置,以及数据流向。我画这张图的时候特意简化了一些细节,方便你理解核心逻辑。

CarNavigationService 系统架构图 地图应用(第三方/预装) CarNavigationManager onNavigationStarted() onNavigationStateChanged() 上报状态 CarNavigationService 接收并解析导航状态 分发到各个系统组件 状态缓存与生命周期管理 分发 中控屏 UI 导航卡片、路线概览 仪表盘 UI 转向提示、距离信息 HUD 显示 简易导航指引 回调 其他车载服务 语音播报、TTS、路线规划 联动

10.5 常见问题与避坑指南

做导航集成的过程中,有几个问题特别容易遇到。我根据自己的经验,整理了一份清单:

问题 原因 解决方案
导航状态不更新 应用没有正确绑定 CarService 检查 Car.createCar() 是否成功,确认权限已授予
仪表盘显示延迟 状态上报频率太低 确保每秒至少上报一次关键状态
导航结束后 UI 未清除 没有调用 onNavigationEnded() 在导航结束的 callback 里务必调用该方法
多屏显示不一致 不同屏幕的刷新机制不同 统一使用 CarNavigationService 分发,不要各自维护状态

曾经踩过的坑:有一次,地图应用在导航结束后没有调用 onNavigationEnded(),结果系统 UI 一直显示「您已到达目的地附近」,用户都开出去五公里了还在显示。排查了半天才发现是应用在退出导航页面时直接 finish() 了 Activity,根本没走导航结束的逻辑。所以,一定要在应用退出前确保状态清理干净。

10.6 性能优化建议

导航状态上报虽然看起来简单,但性能问题不容忽视。我建议你注意以下几点:

  • 批量更新:如果多个状态同时变化(比如距离和时间),合并成一次调用,不要分开调用。
  • 避免高频更新:每秒 2-3 次足够,太频繁反而增加系统负担。
  • 使用异步线程:CarNavigationManager 的方法调用尽量放在子线程,避免阻塞 UI。
  • 内存优化:NavigationState 对象不要频繁创建,可以复用同一个 Builder。

嗯,关于 CarNavigationService 的内容就讲到这里。导航集成看起来简单,但细节很多。你只要把状态上报的时机把握好,生命周期管理好,基本上就不会出大问题。

公众号:蓝海资料掘金营,微信deep3321