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

Jetpack Compose中Firebase Auth登录时协程冻结问题

解决Jetpack Compose中Firebase Auth登录协程冻结及状态丢失问题

一、解决应用冻结问题

问题原因

在Compose的onClick主线程回调中使用runBlocking会直接阻塞主线程,导致UI无法响应。runBlocking会挂起当前线程直到协程执行完毕,而主线程被阻塞后无法处理任何UI事件,最终表现为应用冻结。

修复方案

使用rememberCoroutineScope创建与Composable生命周期绑定的协程作用域,在点击事件中启动协程执行登录逻辑,避免阻塞主线程。

修改后的LoginScreen代码:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoginScreen(navController: NavController) {
    val auth = FirebaseAuth.getInstance()
    val coroutineScope = rememberCoroutineScope()
    val userState = // 你的用户状态变量
    val passwordState = // 你的密码状态变量

    // 仅在页面首次加载时执行一次登出操作
    LaunchedEffect(Unit) {
        auth.signOut()
    }

    // 省略中间UI代码

    Button(
        onClick = {
            val email = userState.toString()
            val password = passwordState.toString()

            if (email.isNotEmpty() && password.isNotEmpty()) {
                coroutineScope.launch {
                    try {
                        signInUser(auth, email, password)
                        // 登录成功直接跳转,无需额外判断currentUser
                        navController.navigate("HomeScreen/$email/$password")
                    } catch (e: Exception) {
                        // 处理登录失败,比如提示用户错误信息
                        // Toast.makeText(LocalContext.current, e.message, Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }
    ) {
        Text("登录")
    }
}

保留原signInUser函数,建议配合异常处理使用:

suspend fun signInUser(auth: FirebaseAuth, email: String, password: String) {
    auth.signInWithEmailAndPassword(email, password).await()
}

二、解决登录状态页面间丢失问题

问题原因

  1. 原代码中auth.signOut()写在Composable根作用域内,每次Compose重组都会执行该操作,强制清除登录状态。
  2. 未正确绑定Firebase Auth状态监听与Compose生命周期,导致状态更新无法及时同步到页面。

修复方案

  1. 仅在登录页面首次加载时执行signOut,通过LaunchedEffect(Unit)实现(已包含在上述代码中)。
  2. 使用AuthStateListener监听登录状态变化,并通过DisposableEffect管理监听的生命周期,避免内存泄漏:

在主导航所在的Composable中添加状态监听逻辑:

@Composable
fun AuthStateHandler(navController: NavController) {
    val auth = FirebaseAuth.getInstance()

    // 缓存监听实例,避免每次重组重复创建
    val authStateListener = remember {
        FirebaseAuth.AuthStateListener { firebaseAuth ->
            val currentUser = firebaseAuth.currentUser
            if (currentUser == null) {
                // 用户未登录,跳转至登录页并清空栈
                navController.navigate("LoginScreen") {
                    popUpTo(navController.graph.startDestinationId) { inclusive = true }
                }
            } else {
                // 用户已登录,确保停留在主页
                if (navController.currentDestination?.route?.startsWith("HomeScreen") != true) {
                    navController.navigate("HomeScreen/${currentUser.email}/***") {
                        popUpTo(navController.graph.startDestinationId) { inclusive = true }
                    }
                }
            }
        }
    }

    // 在Composable生命周期内添加/移除监听
    DisposableEffect(auth) {
        auth.addAuthStateListener(authStateListener)
        onDispose {
            auth.removeAuthStateListener(authStateListener)
        }
    }
}

之后在你的NavHost所在的根Composable中调用AuthStateHandler(navController),即可自动同步登录状态并处理页面跳转。

三、依赖版本一致性调整

将协程相关依赖版本统一,避免版本冲突:

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3")

额外注意事项

  • 不要在Composable根作用域执行会修改全局状态的操作(如signOut),除非明确需要每次重组都执行。
  • 登录成功后无需手动判断auth.currentUser,因为await()会在登录成功后返回,失败则直接抛出异常,可通过try-catch块处理失败场景。
  • 建议捕获Firebase Auth的具体异常类型(如FirebaseAuthInvalidCredentialsException),给用户更精准的错误提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:30:21