如何在 Kotlin 中正确混合协程和回调式实现?

问题描述 投票:0回答:0

我正在尝试在我的应用程序的 MVVM 架构中使用 Firebase 身份验证。我正在使用 Kotlin 和协同程序,在我的挂起函数中实现 firebase 回调后,我遇到了一些与异步执行 Firebase 回调和挂起函数相关的概念性问题。

我正在尝试通过遵循 MVVM 架构在 Kotlin 中使用 Firebase 实现 Google 登录。因此,我设计了一个 AuthViewModel,它通过调用以下挂起函数将接收到的令牌(从 Google One-tap)发送到 AuthRepository:

.
.
.
suspend fun getGoogleSignInResult(token: String): Envelope<AuthUserInfo> {
        return withContext(defaultDispatcher) {
            when(val response = authRemoteDataSource.getGoogleSignInResult(token)) {
                is Envelope.Success -> {
                    Envelope.Success(AuthUserInfo(response.data!!, null))
                }
                else -> {
                    Envelope.Error(response.tag!!, response.message)
                }
            }
        }
    }

此函数分派到 defaultDispatcher 并在我的远程数据源 (authRemoteDataSource) 中调用 authRemoteDataSource.getGoogleSignInResult(token) 并暂停。然后 authRemoteDataSource 获取控制权并分派给 ioDispatcher,如下所示:

.
.
.
suspend fun getGoogleSignInResult(token: String): Envelope<FirebaseUser> {
        return withContext(ioDispatcher) {
            // Got an ID token from Google. Use it to authenticate
            // with Firebase.
            var user: FirebaseUser? = null
            val job = launch {
                val firebaseCredential = GoogleAuthProvider.getCredential(token, null)
                auth.signInWithCredential(firebaseCredential)
                    .addOnCompleteListener { task ->
                        if (task.isSuccessful) {
                            // Sign in success, update UI with the signed-in user's information
                            Log.d("TEST", "signInWithCredential:success")
                            user = auth.currentUser
                        } else {
                            // If sign in fails, display a message to the user.
                            
                            Log.w("TEST", "signInWithCredential:failure", task.exception)
                        }
                    }
            }
            job.join() // callbacks are not synchronous so we must wait for the job
            user?.let { Envelope.Success(user!!) }
                ?: Envelope.Error(Envelope.Tag.NON_CACHED_EXCEPTION)
        }
    }

但是这个函数失败了,因为它在分配给 addOnCompleteListener 的回调完成之前返回; user 为 NULL,它将返回 Envelope.Error(Envelope.Tag.NON_CACHED_EXCEPTION)。似乎对 signInWithCredential 的调用是异步的,作业不在等待它。问题是:

  1. 我可以强制 Firebase 同步执行吗?

顺便说一句,我将上面的代码更改为以下代码,我预计它也不应该工作:

.
.
.
suspend fun getGoogleSignInResult(token: String): Envelope<FirebaseUser> {
        var user: FirebaseUser? = null
        val job =  withContext(ioDispatcher) {
            // Got an ID token from Google. Use it to authenticate
            // with Firebase.
            val firebaseCredential = GoogleAuthProvider.getCredential(token, null)
            auth.signInWithCredential(firebaseCredential)
                .addOnCompleteListener { task ->
                    if (task.isSuccessful) {
                        // Sign in success, update UI with the signed-in user's information
                        Log.d("TEST", "signInWithCredential:success")
                        user = auth.currentUser
                    } else {
                        // If sign in fails, display a message to the user.
                        Log.w("TEST", "signInWithCredential:failure", task.exception)
                    }
                }
        }

        // Return
        job.await() // addOnCompleteListener is not synchronous so we must wait for the job
        return user?.let { Envelope.Success(user!!) }
            ?: Envelope.Error(Envelope.Tag.NON_CACHED_EXCEPTION)
    }

但令人惊讶的是,它可以正常工作;它等待回调 addOnCompleteListener 完成,然后返回 Envelope.Success(user!!)。

2。你知道为什么上面的代码可以正常工作吗?

非常感谢您的帮助。

kotlin mvvm firebase-authentication callback kotlin-coroutines
© www.soinside.com 2019 - 2024. All rights reserved.