You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 11:57:05