如何使用NavController通过DeepLink Uri跳转时传递Bundle参数?
我想实现如下功能:
void nav(NavController navController, Uri deepLink, Bundle args) { navController.navigate(deepLink, args); }
但androidx.navigation.NavController仅提供navigate(Uri deepLink)以及navigate(int resId, Bundle args)等重载方法,并没有navigate(Uri deepLink, Bundle args)方法。请问有没有办法在通过Uri DeepLink跳转至未知Fragment时传递Bundle参数?
比如,如果能通过DeepLink获取目标Fragment的资源ID,就可以调用navController.navigate(resID, args)来实现需求。
背景
我在单Activity应用中使用NavController实现Fragment间跳转,这些Fragment分布在多个模块中,对应不同的nav_graph。DeepLink是通过配置文件中的字符串动态生成,或由服务器下发,格式为类似"{module}://start{name}"的标准形式。部分Fragment需要了解自身的调用来源信息,但大部分不需要,而调用端无法区分两者。
我希望找到一种简洁的方式实现navigate(Uri, Bundle)的功能,已知一些不太优雅的方案(比如修改所有nav_graph添加大部分模块/Fragment不会使用的可选"safeargs"参数,或者将参数Bundle存入全局可访问位置让Fragment在onCreate中获取),但非常希望避免这类方案。
可行解决方案
1. 通过DeepLink匹配获取目标Destination的ID
遍历NavController导航图中的所有节点,匹配对应的DeepLink,找到目标Destination的ID后调用带Bundle的重载方法:
void nav(NavController navController, Uri deepLink, Bundle args) { // 遍历全局导航图的所有节点,匹配DeepLink for (NavDestination destination : navController.getGraph().getNodes()) { boolean isMatch = destination.getDeepLinks().stream() .anyMatch(deepLinkItem -> deepLinkItem.matches(deepLink)); if (isMatch) { navController.navigate(destination.getId(), args); return; } } // 未匹配到目标节点时,降级调用默认DeepLink跳转 navController.navigate(deepLink); }
注意:多模块场景下,需要确保所有模块的nav_graph都已被添加到NavController的全局导航图中,否则会出现匹配失败的情况。
2. 封装扩展函数简化调用
把上述匹配逻辑封装成NavController的扩展函数,让调用形式和你最初期望的完全一致:
fun NavController.navigate(deepLink: Uri, args: Bundle) { val targetDest = graph.nodes.find { dest -> dest.deepLinks.any { it.matches(deepLink) } } targetDest?.let { navigate(it.id, args) } ?: navigate(deepLink) }
调用时直接使用navController.navigate(deepLink, args)即可。
3. 利用NavOptions临时传递参数(非官方推荐)
通过NavOptions的setArguments()方法携带参数,目标Fragment可通过getArguments()获取:
void nav(NavController navController, Uri deepLink, Bundle args) { NavOptions options = new NavOptions.Builder() .setArguments(args) .build(); navController.navigate(deepLink, options); }
注意:如果目标Destination本身通过DeepLink传递了同名参数,NavOptions中的参数会覆盖前者,仅适用于无参数冲突的场景。
内容的提问来源于stack exchange,提问作者Jordi

