Android Navigation库:onSupportNavigateUp中用finish()还是NavController?
咱们先直接给出结论:在你的场景里,在onSupportNavigateUp()中调用finish()关闭DetailActivity是完全可行且合理的,甚至是更直接的正确选择。接下来咱们拆解问题,看看你用NavController遇到问题的原因,以及具体该怎么处理。
为什么你的NavController方式会出错?
先看你的导航图:HomeFragment通过action跳转到DetailActivity,这里的DetailActivity是作为独立的系统Activity实例被启动的(哪怕它被写在导航图的<activity>标签里),它和HomeFragment所在的Activity属于不同的Activity栈节点,各自拥有独立的导航上下文。
你尝试的两种方式问题出在这里:
- 第一种方式:你手动new了一个
NavController(this),这个全新的控制器根本没有关联任何实际的导航栈(既没绑定NavHost,也没处理过任何跳转操作),调用navigateUp()自然会抛出java.lang.IllegalArgumentException: NavController back stack is empty——它确实没有可返回的栈内容。 - 第二种方式:你在BaseActivity中通过
findNavController(R.id.container)获取控制器,但这个控制器对应的是当前Activity(DetailActivity)内部的NavHostFragment(如果你的DetailActivity布局里没有这个NavHost,甚至会直接崩溃),而不是启动它的HomeFragment所在的导航栈,所以它无法帮你回到之前的页面。
正确的处理方式
场景1:DetailActivity是单一页面(无内部导航)
这种情况最简单,直接在onSupportNavigateUp()中调用finish()即可,因为DetailActivity是被添加到系统Activity栈中的,finish()会正确销毁它并回到上一个Activity(也就是HomeFragment所在的页面):
override fun onSupportNavigateUp(): Boolean { finish() return true }
场景2:DetailActivity内部也使用了Navigation Component(有自己的Fragment栈)
如果你的DetailActivity里还有自己的NavHostFragment(比如包含多个子Fragment),那应该先让内部的NavController处理返回,只有当内部栈为空时,再调用finish()关闭整个Activity:
override fun onSupportNavigateUp(): Boolean { val internalNavController = findNavController(R.id.detail_nav_host_fragment) // 先尝试内部导航返回,不行再关闭Activity return internalNavController.navigateUp() || run { finish() true } }
补充说明
Navigation Component的navigateUp()核心作用是处理同一个NavHostFragment下的Fragment栈返回,而跨Activity的跳转本质上还是依赖系统的Activity栈管理。所以当你从Fragment跳转到独立Activity时,用finish()返回是符合设计逻辑的,不需要强行用NavController来处理。
内容的提问来源于stack exchange,提问作者Yamini Balakrishnan

