mcpField
M
migrating-navsdk-7x-to-8x-android

migrating-navsdk-7x-to-8x-android

github.com/googlemaps开发7014 次安装2 天前更新
代码仓库

关于 migrating-navsdk-7x-to-8x-android

这是面向 Android 开发者的 NavSDK 7.x 到 8.0 迁移技能,按阶段指导检查工具链、依赖、破坏性变更和弃用 API。它强调先确保构建环境可用,再处理调用点,并支持仅审计、不修改代码的目标。

主要功能

  1. 01迁移前环境与代码清点
  2. 02分阶段升级工具链和依赖
  3. 03定位必须修复的 API 变更
  4. 04列出弃用项及替代方案
  5. 05支持只审计并报告发现

适用场景

  1. 01将 Android 应用从 NavSDK 7.x 升级到 8.0
  2. 02升级前核对 AGP、Gradle 和 JDK
  3. 03查找导致编译或运行问题的 API 变更
  4. 04在不改代码的情况下评估迁移工作

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/googlemaps/agent-skills --skill migrating-navsdk-7x-to-8x-android

安装后在 Claude Code 中输入/migrating-navsdk-7x-to-8x-android确认已加载。

附带文件references/toolchain_bump.md

详细介绍

将 Android Navigation SDK 从 7.x 迁移到 8.0

请先阅读:哪些内容不会改变

这是同一款 SDK 的版本升级。不要重命名软件包、替换构件,也不要更改导航器的初始化方式。

  • 软件包命名空间仍为 com.google.android.libraries.navigation.*。 不要改写导入语句。
  • 构件仍为 com.google.android.libraries.navigation:navigation。 仅版本号会改变。
  • 初始化方式不变。 继续像以前一样调用 NavigationApi.getNavigator(...)。
  • Navigator.RouteStatus 仍是 Java enum。 不要将其转换为 int 常量。

工作流

按此顺序操作。工具链未配置正确前,8.0 无法编译;在无法配置的构建上修复调用点只会浪费时间。应先处理硬性破坏性变更,再处理弃用项,因为已移除的符号无法编译,而弃用的符号只会产生警告。第 4 阶段是开发者的排期决策,不是强制要求。

第 0 阶段:预检

在进行任何更改前记录以下信息;后续阶段会根据这些信息分支处理:

  • 当前 NavSDK 版本 (com.google.android.libraries.navigation:navigation:<version>)。本指南仅涵盖从 7.x 迁移到 8.0。如果应用使用的是 6.x 或更早版本,请先根据 发行说明 升级到最新的 7.x 版本,确认构建成功后,再继续执行此处的步骤。
  • 构建 DSL(Groovy build.gradle 或 Kotlin build.gradle.kts)、AGP 和 Gradle 版本,以及运行 Gradle 的 JDK。
  • 源代码语言(Java、Kotlin 或两者)。
  • 布局 XML 是否引用 NavSDK 片段,以及应用是否使用 Android Auto(androidx.car.app)。
  • 用户的目标:完整迁移、只需“让它能构建”,还是仅进行审查。对于审查,请报告第 1 至第 4 阶段的检查结果,不做任何修改。

一次性盘点受影响的符号。结果就是其余迁移工作的清单:每个匹配项都对应下方第 1 至第 4 阶段中的某一项;审查时则按阶段分组报告这些匹配项。也要搜索布局 XML,因为 XML 中的片段类名只有在布局膨胀时才会报错。Kotlin 代码通常会推断未来类型(val pending = navigator.setDestination(...)),因此仅搜索 ListenableResultFuture 会漏掉这类情况;搜索 setOnResultListener 可以找到它们。

请在 Java、Kotlin、XML、Gradle 和 ProGuard 文件中搜索以下符号。如果没有 shell,请使用环境提供的代码搜索功能;如果有 shell,可运行以下命令:

grep -rnE "showTrafficLights|showStopSigns|onGpsAvailabilityUpdate|ListenableResultFuture|setOnResultListener|SupportNavigationFragment|setAudioGuidance\(|simulateLocationsAlongNewRoute|MAP_TYPE_TERRAIN|RouteStatus|getNavigator|NavigationTransactionRecorder|FollowMyLocationOptions|header[A-Za-z]*(TypefacePath|TextSize)|kotlin-reflect|kotlin\.reflect|bumptech\.glide|androidx\.car\.app" \
  --include=*.java --include=*.kt --include=*.xml --include=*.gradle --include=*.kts --include=*.pro .
阶段 1:工具链

8.0 的 AAR 元数据声明了 minAndroidGradlePluginVersion=9.0.1。在 AGP 8.x 上,Gradle 会拒绝配置构建:不会显示弃用警告,而是直接失败。请先解决此问题。

如果项目已经使用 AGP >= 9.0.1 和 compileSdk 37,则阶段 1 仅用于验证:确认 Gradle 包装器满足该 AGP 版本的要求,且 JDK 为 17/21,然后继续。不要将较新的 AGP 降级到 9.0.1。

NavSDK 7.x 可在 AGP 9.0.1 / Gradle 9.1.0 / compileSdk 37 上正常构建(已通过发布的 7.9.0 示例应用验证)。仍使用 7.x 时先升级工具链,确保 debug 和 release 构建均成功,然后再更改 NavSDK 版本。这样之后出现的所有失败都可归因于 NavSDK,而非 AGP。

  • Android Gradle 插件:从 8.13.2 升级到 9.0.1。这是硬性最低要求(由 AAR 元数据规定)。
  • Gradle:从 8.13 升级到 9.1.0(适用于 AGP 9.0.1)。更新的 AGP 9.x 需要更新的 Gradle(例如,AGP 9.2.0 需要 Gradle 9.4.1);请遵循 AGP 的错误消息。
  • compileSdk / targetSdk:从 36 升级到 37。SDK 目标版本为 37。
  • Kotlin:2.3.0 升级到 2.3.0。无需更改。
  • desugar_jdk_libs_nio:从 2.0.x 升级到 2.1.5。由 AAR 元数据强制要求。
  • com.google.gms:google-services:从 4.4.x 升级到 4.5.0。仅在使用时需要(AGP 9 移除了 applicationVariants)。

使用 compileSdk 37 时,AGP 9.0.1 会显示“This Android Gradle plugin (9.0.1) was tested up to compile SDK version 36.1”警告。这是预期行为,不会造成影响;如需隐藏此警告,可在 gradle.properties 中添加 android.suppressUnsupportedCompileSdk=37.0(或使用更新的 AGP 9.x)。

注意

不要告知开发者需要 JDK 25。JDK 25 是用于构建 NavSDK 本身的内部 工具链。对于使用 AGP 9 的宿主应用,JDK 17 是 Gradle 运行时的最低要求,建议使用 JDK 21 LTS。

AGP 9 本身引入的行为变更可能会导致构建失败,且看不出与 NavSDK 有明显关联(例如清单中的 package=、kotlinOptions、 dexOptions)。请阅读 references/toolchain_bump.md,查看症状与修复方法列表。请链接到官方的 AGP 9.0 发行说明,而不要重复说明其中的内容。对于同时使用 org.jetbrains.kotlin.multiplatform 和 Android 插件的 Kotlin Multiplatform 模块,请遵循 JetBrains 的 AGP 9 迁移指南 (kotlin-tooling-agp9-migration 技能)进行模块重构,并仍须满足上述 NavSDK 最低要求。

targetSdk 37 会影响 Play Console,但不需要更改代码:使用 NavSDK 的应用必须在 Google Play Console 中完成位置用途声明。选择导航,以说明引导期间需要持续获取精确位置信息 (ACCESS_FINE_LOCATION),并声明任何非导航用途。请告知用户;无法通过代码完成此操作。请参阅 Play Console 位置声明指南。

阶段 2:依赖项

8.0 不再引入若干传递依赖项。体积缩减幅度取决于应用:对于此前未剔除这些依赖项的应用,最多可减少约 4.8 MB;对于已通过 R8(例如完整模式)缩减体积的应用,减少幅度则小得多:

  • org.jetbrains.kotlin:kotlin-reflect
  • com.github.bumptech.glide:glide 和 com.github.bumptech.glide:okhttp3-integration

只有当应用自身的代码引用了它们时,才重新添加其中一项:除 ::class / KClass 基本功能以外的 kotlin.reflect.* API,或 com.bumptech.glide.* 导入。无差别地重新添加会抵消 NavSDK 刚刚实现的体积优化。升级后若这些包出现“找不到类”错误,就是需要重新添加的信号。

反过来,如果应用显式声明了 kotlin-reflect 或 Glide(通常是多年前复制 NavSDK 的传递依赖列表时加上的),但自身代码或 ProGuard 规则中没有使用 kotlin.reflect.* / com.bumptech.glide.*,请删除该声明(以及匹配的 annotationProcessor / kapt / ksp 条目),以实现体积优化。

Android Auto: 8.0 不再捆绑 androidx.car.app 的 ProGuard 保留规则。如果应用使用 Android Auto 投影模式,请将 androidx.car.app:app 和 androidx.car.app:app-projected 升级到 1.3.0 或更高版本,以便应用它们自身的使用方规则。否则,发布版(R8)构建可能会剔除必需的类,而调试版仍能正常运行。

NavSDK 升级的其他库版本(AndroidX lifecycle 2.10.0、material 1.14.0、datastore 1.2.1、window 1.5.1、transport 4.1.1 等)会通过 Gradle 自动解析。只有当应用固定了更旧的版本且依赖解析失败时,才需要采取措施。

阶段 3:重大不兼容变更(必须修复才能编译或正确运行)
  • DisplayOptions.showTrafficLights(...)、showStopSigns(...):已移除。 删除这些调用。在有相关数据的国家和地区,导航期间现在始终会显示交通信号灯和停车标志。
  • RoadSnappedLocationProvider.GpsAvailabilityEnhancedLocationListener.onGpsAvailabilityUpdate(boolean): 已移除。 删除此重写方法;将逻辑移至 onGpsAvailabilityChange(GpsAvailabilityChangeEvent)。遗留的 @Override 会导致编译失败。
  • Navigator.RouteStatus.ROUTING_TIMEOUT:新增枚举常量:路由后端超时。 在应用处理所有状态的地方添加一个分支(见下文)。
  • 导航期间的 GoogleMap.MAP_TYPE_TERRAIN:不再支持。 可以正常编译;运行时,地图会回退到 MAP_TYPE_NORMAL,直到导航结束。请从导航界面中移除地形选项,避免向用户提供实际不会生效的模式。
  • StylingOptions 的 Parcelable 成员(CREATOR、writeToParcel):内部实现,不属于公共 API。 不要对 StylingOptions 进行序列化(例如放入 Bundle / Intent extra)。需要时,请通过其构建器方法重新创建。
  • getNavigator(Application, ...) 返回的 NavigationApi.ErrorCode.NETWORK_ERROR:行为变更:现在可能触发。 在 7.x 中,该错误从未报告。在 8.0 中,当条款检查因网络错误或超时错误而失败,且没有 Activity(即使用 Application 重载)时,onError 会收到 NETWORK_ERROR,而此前收到的是 TERMS_NOT_ACCEPTED。如果 onError 没有 NETWORK_ERROR 分支,请添加一个分支,以便在出错时退避重试 getNavigator,而不是重新提示用户接受条款或将其视为致命错误。保留已有的 NETWORK_ERROR 处理逻辑。

ROUTING_TIMEOUT 和穷尽性处理。 此常量是新增的,但会导致必须穷尽处理所有情况的代码出现问题:

  • 用作表达式的 Kotlin when (status) 在覆盖 ROUTING_TIMEOUT 或添加 else 之前会编译失败。
  • 对该枚举使用 Java switch 表达式(返回值的 -> 形式)也会导致编译错误。
  • 普通的 Java switch 语句可以编译,但会静默跳过新状态。请添加一个 case,让超时得到用户可见的处理(通常是“重试”)。
val message = when (routeStatus) {
    Navigator.RouteStatus.OK -> null
    Navigator.RouteStatus.ROUTING_TIMEOUT -> "Route calculation timed out. Try again."
    Navigator.RouteStatus.NETWORK_ERROR -> "Network error. Check your connection."
    // ...existing branches...
    else -> "Could not calculate a route ($routeStatus)."
}
第 4 阶段:弃用项(在 8.0 中编译时会发出警告;现在修复或安排处理)

请将这些选项呈现给用户。除非用户要求完整迁移,否则不要默默重写每个调用点。对于每一项,请报告其使用位置和移除时间。如果用户要求完整迁移,或你正在以非交互方式运行,请迁移所有有替代方案的弃用项,并将没有替代方案的项目(StylingOptions 字体/文本大小,以及应用需要自定义选项时的 FollowMyLocationOptions)列为待处理项。

  • Navigator.setDestination(s)(...) 返回的 ListenableResultFuture<RouteStatus>:替换为末尾带有 RouteDescriptionLoadCallback、并返回 void 的重载。移除时间:9.0。
  • 返回 ListenableResultFuture 的 Simulator.simulateLocationsAlongNewRoute(...):替换为末尾带有 RouteDescriptionLoadCallback 的重载。移除时间:9.0。
  • SupportNavigationFragment:替换为 NavigationFragment(相同的包、相同的方法、newInstance() / newInstance(GoogleMapOptions))。移除时间:9.0。
  • NavigationTransactionRecorder.pickup(Waypoint, List) / dropoff(Waypoint, List):替换为 pickup(List) / dropoff(List)。移除时间:9.0。
  • Navigator.setAudioGuidance(@AudioGuidance int):替换为 setAudioGuidanceSettings(AudioGuidanceSettings)。移除时间:8.0 发布后约 1 年。
  • StylingOptions 字体路径和文本大小方法(header*TypefacePath、header*TextSize 及 getter):没有替代方案;颜色选项仍然可用。移除时间:尚未公布。
  • GoogleMap.followMyLocation(int, FollowMyLocationOptions) 和 FollowMyLocationOptions 类(com.google.android.gms.maps.model):没有直接替代方案。如果应用不需要自定义选项(例如缩放级别),请使用相同的视角调用 followMyLocation(int);该重载未弃用。否则保留该调用,并将其报告为待处理项。移除时间:未来某个版本(尚未公布)。
将 ListenableResultFuture 替换为 RouteDescriptionLoadCallback

RouteDescriptionLoadCallback 是顶层接口 com.google.android.libraries.navigation.RouteDescriptionLoadCallback,包含一个方法 onRouteDescription(RouteDescription),该方法在 UI 线程上调用。RouteDescription.getRouteStatus() 返回 enum Navigator.RouteStatus。

// Before (deprecated in 8.0)
ListenableResultFuture<Navigator.RouteStatus> future = navigator.setDestination(waypoint);
future.setOnResultListener(status -> {
  if (status == Navigator.RouteStatus.OK) {
    navigator.startGuidance();
  }
});

// After
navigator.setDestination(
    waypoint,
    routeDescription -> {
      if (routeDescription.getRouteStatus() == Navigator.RouteStatus.OK) {
        navigator.startGuidance();
      }
    });
navigator.setDestination(waypoint) { routeDescription ->
    if (routeDescription.routeStatus == Navigator.RouteStatus.OK) {
        navigator.startGuidance()
    }
}

每个 setDestination / setDestinations 重载(带有 RoutingOptions、DisplayOptions、CustomRoutesOptions)都有一个参数开头部分相同的回调版本。

将 SupportNavigationFragment 替换为 NavigationFragment

请在代码和布局 XML 中都替换该类,涵盖 <fragment> / FragmentContainerView 上的 android:name="..." 和 class="..." 属性。仅替换代码虽然可以顺利编译,但随后会在布局填充时崩溃,或者会让 XML 中仍然保留已弃用的类。

<fragment
    android:id="@+id/navigation_fragment"
    android:name="com.google.android.libraries.navigation.NavigationFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

与 SupportNavigationFragment 一样,NavigationFragment 也是 AndroidX Fragment,因此 supportFragmentManager 的用法无需更改。请更新所有引用,而不只是导入项:字段/属性类型、强制类型转换(例如 (SupportNavigationFragment) getSupportFragmentManager().findFragmentById(...))、instanceof / Kotlin is 检查、SupportNavigationFragment() 构造函数调用、SupportNavigationFragment.newInstance(...) 调用,以及 KDoc/Javadoc 中的 [SupportNavigationFragment] 链接。请参阅更新为 NavigationFragment。

setAudioGuidance(int) 改为 setAudioGuidanceSettings(...)

默认值正好相反。旧标志位掩码从零开始累加,但 AudioGuidanceSettings.builder() 默认启用振动、 GuidanceMode.VOICE_ALERTS_AND_GUIDANCE、蓝牙音频和 VolumeLevel.NORMAL。对于旧标志位掩码中未包含的设置,请显式关闭。

将每个 Navigator.AudioGuidance 标志映射到一个 AudioGuidanceSettings.Builder 调用:

  • SILENT:setGuidanceMode(GuidanceMode.SILENT)
  • VOICE_ALERTS_ONLY:setGuidanceMode(GuidanceMode.VOICE_ALERTS_ONLY)
  • VOICE_ALERTS_AND_GUIDANCE: setGuidanceMode(GuidanceMode.VOICE_ALERTS_AND_GUIDANCE)
  • 已设置 / 未设置 VIBRATION:setVibrationEnabled(true) / setVibrationEnabled(false)
  • 已设置 / 未设置 BLUETOOTH_AUDIO:setBluetoothAudioEnabled(true) / setBluetoothAudioEnabled(false)
// Before: navigator.setAudioGuidance(
//     Navigator.AudioGuidance.VOICE_ALERTS_AND_GUIDANCE | Navigator.AudioGuidance.BLUETOOTH_AUDIO);
navigator.setAudioGuidanceSettings(
    AudioGuidanceSettings.builder()
        .setGuidanceMode(AudioGuidanceSettings.GuidanceMode.VOICE_ALERTS_AND_GUIDANCE)
        .setBluetoothAudioEnabled(true)
        .setVibrationEnabled(false) // VIBRATION was not in the old bitmask
        .build());
第 5 阶段:验证并报告
  1. 构建调试版和发布版。仅由 R8 导致的问题(Android Auto 保留规则、AGP 9 下禁止使用的消费者保留规则)只会在发布版中出现。
  2. 显示弃用警告:Kotlin 默认会打印 w: 行;对于 Java,可将 options.compilerArgs += ["-Xlint:deprecation"] 添加到 tasks.withType(JavaCompile) 中(或查看每个文件的“使用或覆盖了已弃用 API”提示)。重新运行第 0 阶段的 grep。已移除的符号必须全部消失。任何仍在使用的已弃用 API,都必须是用户选择暂缓迁移的项目。
  3. 向用户简要报告:
    • 更改内容(工具链、依赖项、调用位置)。
    • 暂缓迁移的弃用 API,包括文件位置和移除版本 (9.0、约 1 年后或未来版本)。
    • 非代码后续事项:Play 管理中心的位置用途声明,以及可选的新型偏离路线交通事故卡片。启用交通图层时,这些卡片默认开启。如果应用的 UX 需要关闭,可在 NavigationView / NavigationFragment 上调用 setTrafficIncidentCardsEnabled(false)。

验证清单

  • AGP >= 9.0.1,并使用 AGP 要求的 Gradle 版本;Gradle JDK >= 17;compileSdk / targetSdk 为 37;NavSDK 依赖项为 8.0.x。
  • 导入仍为 com.google.android.libraries.navigation.*; NavigationApi.getNavigator(...) 初始化方式未变;RouteStatus 仍作为枚举使用。
  • 不再引用 showTrafficLights、showStopSigns 或 onGpsAvailabilityUpdate。
  • 对 RouteStatus 的穷举处理涵盖 ROUTING_TIMEOUT; 保留对 RouteStatus.NETWORK_ERROR 的处理。
  • getNavigator 的 onError 会处理 ErrorCode.NETWORK_ERROR 并重试 (现在 getNavigator(Application, ...) 也可能触发该错误)。
  • 仅当应用直接使用时才声明 kotlin-reflect / Glide; 如果使用 Android Auto,则 androidx.car.app >= 1.3.0。
  • 不对 StylingOptions 进行序列化;导航期间不提供地形图层。
  • 已迁移弃用 API(包括布局 XML 中的 SupportNavigationFragment),或在报告中列出并说明暂缓迁移。
  • 调试版和发布版构建均成功;已提醒用户填写 Play 位置用途声明。

评论

支持 Markdown