Android:Firebase邮箱验证登录跳转异常问题求助
问题:Firebase Auth登录后邮箱验证状态判断异常
Android应用集成Firebase Auth,注册流程正常:用户注册后信息存入Firebase,发送邮箱验证,验证后点击应用内“继续”,应用检测邮箱已验证则跳转首页,未验证则提示错误。
但退出登录后再次登录时出现异常:逻辑设定为邮箱已验证则跳转首页,否则进入验证页面。尽管isEmailVerified变量实际为true,却始终跳转到验证页面,同时日志显示reloadFirebaseUser方法抛出kotlinx.coroutines.JobCancellationException协程取消异常。
登录Composable代码
Box(modifier = Modifier.fillMaxSize()) { Column( ..... ) { OutlinedTextField( ... label = { Text(text = "Email") } ) OutlinedTextField( ... label = { Text(text = "Password") }, LoadingTextButton( showLoadingState = viewModel.showLoadingState, text = "Sign In", padding = 16.dp ) { viewModel.onSignInWithEmailAndPasswordClick() } when (val signInResponse = viewModel.emailAndPasswordSignInResponse) { is ResponseEmailPassword.Loading -> { viewModel.showLoadingState.value = true } is ResponseEmailPassword.Success -> { viewModel.showLoadingState.value = false val isUserSignedIn = signInResponse.data LaunchedEffect(key1 = isUserSignedIn) { if (isUserSignedIn) { viewModel.isEmailVerified() delay(500L) val isEmailVerified = viewModel.isEmailVerified Log.d("isEmailVerified", "sign in View: ${viewModel.isEmailVerified}") if (isEmailVerified) { navController.navigate("home_Screen") } else { navController.navigate("verifiy_email_Screen") } } } } is ResponseEmailPassword.Failure -> signInResponse.apply { viewModel.showLoadingState.value = false Log.d("checkSignIN", authErrors(e.message)) viewModel.errorMessage.value = authErrors(e.message) } }
ViewModel代码
@HiltViewModel class SignInViewModel @Inject constructor( private val emailPasswordRepo: EmailPasswordAuthRepository ) : ViewModel() { var emailAndPasswordSignInResponse by mutableStateOf<SignInResponse>(ResponseEmailPassword.Success(false)) var showLoadingState = mutableStateOf(false) var isEmailVerified by mutableStateOf(false) fun isEmailVerified() { showLoadingState.value = true viewModelScope.launch { emailPasswordRepo.reloadFirebaseUser() isEmailVerified = emailPasswordRepo.currentUser?.isEmailVerified ?: false Log.d("isEmailVerified", "from sign in viewModel $isEmailVerified") showLoadingState.value = false } } fun onSignInWithEmailAndPasswordClick() = viewModelScope.launch { emailAndPasswordSignInResponse = ResponseEmailPassword.Loading emailAndPasswordSignInResponse = emailPasswordRepo.firebaseSignInWithEmailAndPassword( emailTextFieldValue.value, passwordTextFieldValue.value ) }
Repository代码
@Singleton class EmailPasswordAuthRepositoryImpl @Inject constructor( private val auth: FirebaseAuth ) : EmailPasswordAuthRepository { override val currentUser: FirebaseUser? get() = auth.currentUser override suspend fun firebaseSignInWithEmailAndPassword( email: String, password: String ): SignInResponse { return try { auth.signInWithEmailAndPassword(email, password).await() ResponseEmailPassword.Success(true) } catch (e: Exception) { ResponseEmailPassword.Failure(e) } } override suspend fun reloadFirebaseUser(): ReloadUserResponse { return try { auth.currentUser?.reload()?.await() Log.d("isEmailVerified", "reloadFirebaseUser: user reloaded ${auth.currentUser?.isEmailVerified}") ResponseEmailPassword.Success(true) } catch (e: Exception) { Log.d("isEmailVerified", "reloadFirebaseUser Failure: ${e}") ResponseEmailPassword.Failure(e) } }
运行日志
reloadFirebaseUser Failure: kotlinx.coroutines.JobCancellationException: Job was cancelled;job=SupervisorJobImpl{Cancelling}@fb30f15 from sign in viewModel true
解决方案
1. 移除硬编码延迟,改用状态监听
当前代码依赖delay(500L)等待状态更新,这不可靠且容易因协程取消导致读取旧状态。修改登录Composable中的逻辑,拆分状态监听:
when (val signInResponse = viewModel.emailAndPasswordSignInResponse) { is ResponseEmailPassword.Loading -> { viewModel.showLoadingState.value = true } is ResponseEmailPassword.Success -> { viewModel.showLoadingState.value = false val isUserSignedIn = signInResponse.data LaunchedEffect(key1 = isUserSignedIn) { if (isUserSignedIn) { viewModel.isEmailVerified() } } } // 保持Failure分支不变 } // 单独监听isEmailVerified状态变化,触发跳转 LaunchedEffect(key1 = viewModel.isEmailVerified) { // 确保是登录成功后才处理跳转 if (viewModel.emailAndPasswordSignInResponse is ResponseEmailPassword.Success && (viewModel.emailAndPasswordSignInResponse as ResponseEmailPassword.Success).data) { if (viewModel.isEmailVerified) { navController.navigate("home_Screen") } else { navController.navigate("verifiy_email_Screen") } } }
2. 处理协程取消异常
在Repository的reloadFirebaseUser中单独捕获JobCancellationException,避免影响后续状态获取:
override suspend fun reloadFirebaseUser(): ReloadUserResponse { return try { auth.currentUser?.reload()?.await() Log.d("isEmailVerified", "reloadFirebaseUser: user reloaded ${auth.currentUser?.isEmailVerified}") ResponseEmailPassword.Success(true) } catch (e: JobCancellationException) { Log.d("isEmailVerified", "reloadFirebaseUser cancelled: ${e}") // 协程取消时仍尝试获取当前用户的验证状态 val isVerified = auth.currentUser?.isEmailVerified ?: false Log.d("isEmailVerified", "reload cancelled but current state: $isVerified") ResponseEmailPassword.Success(isVerified) } catch (e: Exception) { Log.d("isEmailVerified", "reloadFirebaseUser Failure: ${e}") ResponseEmailPassword.Failure(e) } }
3. 优化ViewModel状态更新逻辑
确保无论reloadFirebaseUser是否成功,都能更新isEmailVerified状态:
fun isEmailVerified() { showLoadingState.value = true viewModelScope.launch { // 执行reload,忽略响应结果,直接获取最新状态 runCatching { emailPasswordRepo.reloadFirebaseUser() } isEmailVerified = emailPasswordRepo.currentUser?.isEmailVerified ?: false Log.d("isEmailVerified", "from sign in viewModel $isEmailVerified") showLoadingState.value = false } }
核心原因说明
问题根源在于:LaunchedEffect中的delay(500L)可能在isEmailVerified更新前就执行了跳转判断,读取到初始的false值;同时reloadFirebaseUser的协程取消异常导致流程中断,但后续状态更新仍成功,此时跳转逻辑已经执行完毕,导致错误跳转。通过状态监听替代固定延迟,可确保在状态更新后再执行跳转。
内容的提问来源于stack exchange,提问作者Ayman Ait
相关产品推荐
相关产品推荐

