关于 migrating-navsdk-7x-to-8x-android
这是面向 Android 开发者的 NavSDK 7.x 到 8.0 迁移技能,按阶段指导检查工具链、依赖、破坏性变更和弃用 API。它强调先确保构建环境可用,再处理调用点,并支持仅审计、不修改代码的目标。
主要功能
- 01迁移前环境与代码清点
- 02分阶段升级工具链和依赖
- 03定位必须修复的 API 变更
- 04列出弃用项及替代方案
- 05支持只审计并报告发现
适用场景
- 01将 Android 应用从 NavSDK 7.x 升级到 8.0
- 02升级前核对 AGP、Gradle 和 JDK
- 03查找导致编译或运行问题的 API 变更
- 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仍是 Javaenum。 不要将其转换为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或 Kotlinbuild.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-reflectcom.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/Intentextra)。需要时,请通过其构建器方法重新创建。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 阶段:验证并报告
- 构建调试版和发布版。仅由 R8 导致的问题(Android Auto 保留规则、AGP 9 下禁止使用的消费者保留规则)只会在发布版中出现。
- 显示弃用警告:Kotlin 默认会打印
w:行;对于 Java,可将options.compilerArgs += ["-Xlint:deprecation"]添加到tasks.withType(JavaCompile)中(或查看每个文件的“使用或覆盖了已弃用 API”提示)。重新运行第 0 阶段的 grep。已移除的符号必须全部消失。任何仍在使用的已弃用 API,都必须是用户选择暂缓迁移的项目。 - 向用户简要报告:
- 更改内容(工具链、依赖项、调用位置)。
- 暂缓迁移的弃用 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 位置用途声明。