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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 20:47:48