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

Clean架构下Kotlin Flow结合Firebase Auth校验邮箱崩溃及状态返回问题

问题分析与解决方案

你的应用崩溃的核心原因是:在Firebase的addOnCompleteListener回调中抛出的异常,不在协程上下文范围内,无法被CheckUserUseCase中的try-catch捕获,导致异常直接逃逸到应用进程层面引发崩溃。

下面是两种可行的修复方案,推荐第二种更符合业务语义的实现方式:


方案1:修正异常抛出的上下文

保持现有接口定义不变,调整Firebase层的实现,确保异常在协程上下文内抛出:

修改FirebaseAuthentication.kt

class FirebaseAuthentication @Inject constructor(private val auth: FirebaseAuth) : FirebaseUserActions {
    override suspend fun isUserExists(mail: String) {
        // 直接用await()获取Task结果,无需回调监听器
        val taskResult = auth.fetchSignInMethodsForEmail(mail).await()
        val signInMethods = taskResult.signInMethods
        
        if (signInMethods.isNullOrEmpty()) {
            // 此时异常在协程函数内抛出,会被上层UseCase的try-catch捕获
            throw IOException("User with this email does not exist")
        }
    }
}

方案2:调整接口语义(推荐)

将“用户不存在”作为业务结果返回,而非通过异常传递,代码逻辑更直观:

1. 修改SignInRepository.kt接口

interface SignInRepository {
    // 直接返回Boolean表示用户是否存在,无需抛异常
    suspend fun isUserExists(mail: String): Boolean
}

2. 修改SignInRepositoryImpl.kt

class SignInRepositoryImpl @Inject constructor(private val firebaseUserActions: FirebaseUserActions) : SignInRepository {
    override suspend fun isUserExists(mail: String): Boolean {
        return firebaseUserActions.isUserExists(mail)
    }
}

3. 修改FirebaseAuthentication.kt

class FirebaseAuthentication @Inject constructor(private val auth: FirebaseAuth) : FirebaseUserActions {
    override suspend fun isUserExists(mail: String): Boolean {
        val taskResult = auth.fetchSignInMethodsForEmail(mail).await()
        val signInMethods = taskResult.signInMethods
        // 直接返回判断结果,业务逻辑更清晰
        return !signInMethods.isNullOrEmpty()
    }
}

4. 优化CheckUserUseCase.kt

class CheckUserUseCase @Inject constructor(private val repository: SignInRepository) {
    operator fun invoke(mail: String): Flow<Status<Boolean, String>> = flow {
        emit(Status.Loading(data = null))
        try {
            val userExists = repository.isUserExists(mail)
            emit(Status.Success(data = userExists))
        } catch (e: Exception) {
            // 仅处理Firebase请求过程中的异常(如网络错误)
            emit(Status.Error(message = e.message ?: "Unknown error", data = false))
        }
    }
}

核心注意点

  • 避免在Firebase的回调监听器(如addOnCompleteListener)中抛出异常,这类回调运行在独立线程,不属于协程上下文,异常无法被上层的try-catch捕获。
  • 使用Task.await()将异步操作转换为协程可感知的挂起函数,所有逻辑都在协程上下文内执行,异常才能被正确捕获和处理。

内容的提问来源于stack exchange,提问作者r3cc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:45:37