在Activity的onCreate中使用NavHostController导航遇空指针问题求解
你的空指针异常根源在于:setContent内的Compose代码并非同步执行,navController = rememberNavController()会在Compose的Composition阶段才完成赋值,而onCreate里的when语句会在这之前执行,此时navController还未初始化,触发lateinit变量未初始化异常。
下面提供几种实用的解决方案:
方案一:直接设置初始路由(最简洁)
根据条件直接指定NavHost的startDestination,跳过后续手动导航步骤:
private lateinit var navController: NavHostController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 提前计算初始路由 val initialRoute = if (intent?.extras?.getBoolean(Constants.ONBOARDING) == true) { // 替换为你的引导页路由 OnBoardingScreenType.OnBoarding.route } else { LoginScreenType.ForgotPassword.route } setContent { navController = rememberNavController() LearEnglishTheme { NavHost(navController, startDestination = initialRoute) { addLoginScreen(navController, viewModel) addForgotPasswordScreen(navController) addTrialStudentScreen(navController) // 别忘了注册引导页的路由 addOnBoardingScreen(navController) } } } }
方案二:用状态监听触发导航(适合动态场景)
通过MutableState保存导航条件,在Compose内部用LaunchedEffect监听状态变化,此时navController已完成初始化:
private val shouldNavigateToForgotPassword = mutableStateOf(false) private val shouldShowOnBoarding = mutableStateOf(false) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val showOnBoarding: Boolean = intent?.extras?.getBoolean(Constants.ONBOARDING) == true when { showOnBoarding -> shouldShowOnBoarding.value = true else -> shouldNavigateToForgotPassword.value = true } setContent { navController = rememberNavController() LearEnglishTheme { NavHost(navController, startDestination = LoginScreenType.Login.route) { addLoginScreen(navController, viewModel) addForgotPasswordScreen(navController) addTrialStudentScreen(navController) addOnBoardingScreen(navController) } // 监听忘记密码导航触发 LaunchedEffect(shouldNavigateToForgotPassword.value) { if (shouldNavigateToForgotPassword.value) { navController.navigate(LoginScreenType.ForgotPassword.route) { // 替换当前栈,避免返回登录页 popUpTo(LoginScreenType.Login.route) { inclusive = true } } } } // 监听引导页导航触发 LaunchedEffect(shouldShowOnBoarding.value) { if (shouldShowOnBoarding.value) { navController.navigate(OnBoardingScreenType.OnBoarding.route) { popUpTo(LoginScreenType.Login.route) { inclusive = true } } } } } } }
方案三:ViewModel事件驱动(适合复杂业务)
通过ViewModel的SharedFlow发送导航事件,解耦Activity与Compose的导航逻辑:
第一步:创建ViewModel
class LoginViewModel : ViewModel() { private val _navEvent = MutableSharedFlow<String>() val navEvent = _navEvent.asSharedFlow() fun triggerNavigation(route: String) { viewModelScope.launch { _navEvent.emit(route) } } }
第二步:在Activity中使用
private val viewModel: LoginViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val showOnBoarding: Boolean = intent?.extras?.getBoolean(Constants.ONBOARDING) == true when { showOnBoarding -> viewModel.triggerNavigation(OnBoardingScreenType.OnBoarding.route) else -> viewModel.triggerNavigation(LoginScreenType.ForgotPassword.route) } setContent { val navController = rememberNavController() LearEnglishTheme { NavHost(navController, startDestination = LoginScreenType.Login.route) { addLoginScreen(navController, viewModel) addForgotPasswordScreen(navController) addTrialStudentScreen(navController) addOnBoardingScreen(navController) } // 收集导航事件并执行跳转 LaunchedEffect(Unit) { viewModel.navEvent.collect { route -> navController.navigate(route) { popUpTo(LoginScreenType.Login.route) { inclusive = true } } } } } } }
注意事项
- 调用
navigate时建议添加popUpTo参数,避免栈内保留多余页面 - 不要在Compose外部直接持有
NavHostController并调用导航,Compose生命周期与Activity不完全同步,容易出现未初始化或已销毁问题
内容的提问来源于stack exchange,提问作者arun
相关产品推荐
相关产品推荐

