Navigation Component跨模块跳转最佳实践咨询
这是安卓多模块开发经过大量项目验证的通用落地方案,无过度设计,可完全实现模块间零耦合,同时适配View、Compose体系下的Navigation组件场景。
核心实现逻辑
核心思路是模块内导航逻辑完全私有,对外只暴露统一路由协议,通过Navigation原生支持的深链接机制完成跳转,全程不需要跨模块依赖页面类、导航资源:
- 先抽离一个所有业务模块都依赖的公共基础模块
common-router,这个模块绝对不依赖任何业务代码,只承担两个职责:- 定义所有跨模块跳转的路由声明,用密封类统一管理路径、参数,避免硬编码路径导致的拼写错误:
// common-router 模块内代码 sealed class RoutePath(val path: String) { data class OrderDetail(val orderId: String) : RoutePath("order/detail") data class UserProfile(val userId: Long) : RoutePath("user/profile") // 所有需要对外暴露的跨模块页面,都在这里统一声明 }- 封装统一的跳转入口,业务方不需要自行拼接Uri、处理通用导航逻辑:
// 全局统一跳转扩展方法,业务方直接调用即可 fun NavController.navigateTo(route: RoutePath) { val uriBuilder = Uri.parse("app://internal/${route.path}").buildUpon() // 自动将路由对象的字段拼接为请求参数 route::class.memberProperties.forEach { prop -> uriBuilder.appendQueryParameter(prop.name, prop.get(route).toString()) } // 直接传Uri导航,Navigation组件会自动匹配全应用中声明了对应深链接的页面 navigate(uriBuilder.build()) } - 每个业务模块维护自己的独立Navigation图,只给需要对外暴露的页面配置匹配规则的隐式深链接,配置完全属于模块内部逻辑,其他模块无感知:
View体系XML导航图配置示例:
Compose Navigation配置示例:<!-- 订单模块私有navigation xml内的配置 --> <fragment android:id="@+id/fragment_order_detail" android:name="com.xxx.biz.order.OrderDetailFragment"> <!-- 配置和路由规则匹配的深链接 --> <deepLink app:uri="app://internal/order/detail?orderId={orderId}" /> <argument android:name="orderId" app:argType="string" /> </fragment>// 用户模块内部的NavHost配置 composable( route = "internal/user/profile", deepLinks = listOf(navDeepLink { uriPattern = "app://internal/user/profile?userId={userId}" }), arguments = listOf(navArgument("userId") { type = NavType.LongType }) ) { backStackEntry -> UserProfilePage(backStackEntry.arguments?.getLong("userId")) } - 业务代码调用非常简单,比如首页模块要跳订单详情,不需要依赖订单模块的任何代码,直接写:
findNavController().navigateTo(RoutePath.OrderDetail(orderId = "ORD20240001"))
行业通用避坑准则
- 绝对禁止跨模块直接引用其他模块的Fragment/Activity类、
R.id.xxx_fragment/R.navigation.xxx这类资源ID,只要出现这类硬引用,模块隔离就完全失效,后续模块重构、改包名会触发全量编译报错,维护成本极高。 - 跨模块跳转只传基础类型、简单可序列化数据,禁止直接传大对象、回调接口,大对象统一通过内存缓存、本地存储传索引key,避免触发
TransactionTooLargeException,也防止参数类版本不一致导致类找不到崩溃。 - 所有全局拦截逻辑(登录态校验、权限校验、路由降级兜底)统一在
common-router的跳转入口处处理,不要散落在各个业务页面里,比如跳个人页需要登录,没登录就直接先跳登录页,登录完成后自动续跳目标页,业务模块不需要感知这类公共逻辑。 - 对外暴露的路由路径做兼容处理,不要随意修改路径、删除参数,必须调整路径时要加映射规则,老路径跳转自动重定向到新路径,避免版本迭代后老的跳转逻辑失效。
- 不要搞全模块合并的巨型Navigation图,这种做法会把所有模块的导航逻辑强耦合在一起,随着业务扩张XML/路由配置会变得完全不可维护,也失去了多模块拆分支持独立编译、按需加载的意义。
- 如果需要支持外部H5、系统通知跳原生页面,直接复用这套深链接规则即可,不需要额外做一套路由逻辑,维护成本极低。
内容的提问来源于stack exchange,提问作者kokoko
相关产品推荐
相关产品推荐

