我尝试在我的应用程序中的MVVM架构中使用Firebase身份验证。我使用Kotlin和协程,在我的暂停函数中实现Firebase回调后,我遇到了一些与Firebase回调和暂停函数的异步执行相关的概念问题。
我试图通过遵循MVVM架构在Kotlin中实现Google Signin with Firebase。因此,我设计了一个AuthViewModel,它通过调用以下suspend函数将接收到的令牌(从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.你知道为什么上面的代码可以正常工作吗?
非常感谢你的帮助。
1条答案
按热度按时间lp0sw83n1#
我使用suspendCoroutine将回调和suspend函数结合起来,现在我修改了代码如下:
这是正确的。我使用 addOnSuccessListener 和 addOnFailureListener 来获取结果,而不是 addOnCompleteListener。通过使用suspendCoroutine,函数 getGoogleSignInResult 应该被挂起,直到在其中一个回调中调用continuation.resume(res)。然后它恢复并返回 res。