屏幕旋转后如何保留Jetpack Navigation NavHost的状态与回退栈?
核心错误分析
- 强制重复导航:每次Activity重建(包括旋转)都调用
navController.navigate(MAIN_PAGE),直接覆盖了恢复后的导航状态,导致回到初始页面。 - 错误的状态保存方式:Activity无法直接获取Composable内部创建的
NavHostController实例,之前的onSaveInstanceState代码无法正确拿到导航状态完成保存。
正确实现方案
方案1:利用rememberNavController自动状态保存(推荐)
rememberNavController()内部已经集成了状态保存逻辑,只需避免强制重复导航即可:
修改MainActivity代码
class MainActivity : ComponentActivity() { private lateinit var viewModel: OurViewModel @Inject lateinit var ourViewModelFactory: OurViewModelFactory override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val appComponent = DaggerAppComponent.create() appComponent.inject(this) viewModel = ViewModelProvider(this, ourViewModelFactory).get(OurViewModel::class.java) setContent { OurTheme { val navController = rememberNavController() ThisScreenNavigation(navController, viewModel) // 仅在首次创建时执行初始导航(可选,NavHost的startDestination已默认处理) LaunchedEffect(Unit) { if (savedInstanceState == null) { // 注释原强制导航,NavHost的startDestination已设置为MAIN_PAGE // navController.navigate(MAIN_PAGE) } } } } } }
保持ThisScreenNavigation代码不变
@Composable fun ThisScreenNavigation(navController: NavHostController, viewModel : OurViewModel) { NavHost(navController, startDestination = MAIN_PAGE) { composable(route = MAIN_PAGE) { MainPage(navController, viewModel) } composable(route = SECOND_PAGE) { SecondPage(navController, viewModel) } } }
方案2:通过ViewModel手动保存导航状态(适合复杂场景)
如果需要更灵活的状态控制,可以将导航状态保存在ViewModel中(ViewModel旋转后存活):
修改ViewModel代码
class OurViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { private val NAV_STATE_KEY = "nav_controller_state" // 保存导航状态 fun saveNavState(state: Bundle) { savedStateHandle.set(NAV_STATE_KEY, state) } // 获取保存的导航状态 fun getSavedNavState(): Bundle? { return savedStateHandle.get(NAV_STATE_KEY) } }
修改ThisScreenNavigation代码
@Composable fun ThisScreenNavigation(navController: NavHostController, viewModel : OurViewModel) { // 恢复导航状态 LaunchedEffect(Unit) { viewModel.getSavedNavState()?.let { navController.restoreState(it) } } // 监听导航变化,实时保存状态 DisposableEffect(navController) { val listener = NavController.OnDestinationChangedListener { _, _, _ -> navController.saveState()?.let { viewModel.saveNavState(it) } } navController.addOnDestinationChangedListener(listener) onDispose { navController.removeOnDestinationChangedListener(listener) } } NavHost(navController, startDestination = MAIN_PAGE) { composable(route = MAIN_PAGE) { MainPage(navController, viewModel) } composable(route = SECOND_PAGE) { SecondPage(navController, viewModel) } } }
MainActivity代码同方案1
移除强制的navController.navigate(MAIN_PAGE),仅在首次创建时可选执行。
关键注意事项
- 不要在每次重组时调用
navController.navigate(),否则会覆盖恢复后的状态。 rememberNavController()依赖Compose的状态保存机制,确保你的Composable树正确关联到Activity的生命周期。- 若使用ViewModel保存状态,需确保ViewModel通过
SavedStateHandle持久化状态,避免进程被杀后状态丢失。
内容的提问来源于stack exchange,提问作者Ali Has
相关产品推荐
相关产品推荐

