Kotlin Compose中如何在挂起函数doLogout完成后执行代码?
解决注销完成后再更新登录状态的问题
你的核心问题是当前代码中isLogin = false会在注销逻辑完成前就执行,原因是onLogout内部用了回调嵌套协程的写法,导致无法跟踪注销流程的结束时机。下面给出两种可行的改造方案:
方案一:让onLogout成为挂起函数,UI层协程等待完成
第一步:将Firebase获取Token的回调转为协程友好的挂起函数
Firebase的token方法是回调式的,我们可以用suspendCoroutine包装成挂起函数,避免嵌套回调:
suspend fun FirebaseMessaging.getTokenSuspend(): String? { return suspendCoroutine { continuation -> token.addOnCompleteListener { task -> if (task.isSuccessful) { continuation.resume(task.result) } else { continuation.resumeWithException(task.exception ?: Exception("获取FCM Token失败")) } } } }
第二步:改造ViewModel的onLogout为挂起函数
把原来嵌套的协程和回调逻辑整理成线性的协程代码,确保所有注销逻辑执行完成后,函数才会返回:
suspend fun onLogout() { // 先更新ViewModel内的状态到主线程 withContext(Dispatchers.Main) { loginStatus = "out" getUserType = null } try { // 以挂起方式获取FCM Token val token = FirebaseMessaging.getInstance().getTokenSuspend() token?.let { // 执行仓库层的注销逻辑,等待其完成 repo.doLogout(it) } } catch (exp: Exception) { println("FCM token error") } }
第三步:UI层在协程中调用并等待完成
在UI的点击事件里,用生命周期协程作用域启动协程,等待onLogout执行完毕后再设置isLogin = false:
// Jetpack Compose示例 .clickable { lifecycleScope.launch { loginViewModel.onLogout() // 注销完成后才执行这行 isLogin = false } } // 传统View系统示例 logoutButton.setOnClickListener { lifecycleScope.launch { loginViewModel.onLogout() isLogin = false } }
方案二:用Flow发送注销完成事件(适合需要监听状态的场景)
如果需要在多个地方监听注销完成事件,可以用SharedFlow在ViewModel中发送完成信号:
第一步:ViewModel中定义完成事件Flow
private val _logoutCompleted = MutableSharedFlow<Unit>() // 对外暴露不可变的Flow val logoutCompleted = _logoutCompleted.asSharedFlow() fun onLogout() { viewModelScope.launch { withContext(Dispatchers.Main) { loginStatus = "out" getUserType = null } try { val token = FirebaseMessaging.getInstance().getTokenSuspend() token?.let { repo.doLogout(it) } // 注销完成后发送事件 _logoutCompleted.emit(Unit) } catch (exp: Exception) { println("FCM token error") } } }
第二步:UI层监听完成事件
// Jetpack Compose示例 LaunchedEffect(loginViewModel.logoutCompleted) { loginViewModel.logoutCompleted.collect { isLogin = false } } // 点击事件只需触发注销 .clickable { loginViewModel.onLogout() } // 传统View系统示例 lifecycleScope.launch { loginViewModel.logoutCompleted.collect { isLogin = false } } logoutButton.setOnClickListener { loginViewModel.onLogout() }
为什么原代码会出问题?
原onLogout函数中,FirebaseMessaging.getInstance().token.addOnCompleteListener是异步回调,内部又启动了一个新的协程执行repo.doLogout,这导致onLogout函数本身会立刻返回,UI层的isLogin = false会在注销逻辑还没跑完时就执行,完全达不到你要的时序要求。
内容的提问来源于stack exchange,提问作者Cipri
相关产品推荐
相关产品推荐

