我正在尝试在我的应用程序的 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 的调用是异步的,作业不在等待它。问题是:
顺便说一句,我将上面的代码更改为以下代码,我预计它也不应该工作:
.
.
.
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。你知道为什么上面的代码可以正常工作吗?
非常感谢您的帮助。