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
相关产品推荐
相关产品推荐

