我的目标是让 loadInformationFromFirestore(从 Firestore 数据库读取数据)在执行其后的代码(此处只是 Timber 日志记录消息)之前完成。当我运行 getGroupSummaries 时,这些是我看到的日志消息:
runBlocking 中 loadUserInformationFromFirestore 之后
runBlocking 后 loadUserInformationFromFirestore 后
在从Firestore加载用户信息
这对我来说“runBlocking”根本没有阻塞——runBlocking 在 LoadUserInformationFromFirestore 完成之前返回。任何建议将不胜感激。
var mFirestore: FirebaseFirestore? = null
fun getGroupSummaries(groupInfo: GroupInformation, adminUserInfo: UserInformation) {
mFirestore = FirebaseFirestore.getInstance()
val uid = "3iHWCsGmKvfq4w9OgDNWZsSs8wy2"
runBlocking {
val loadUserInformationJob = viewModelScope.launch { loadUserInformationFromFirestore(uid) }
loadUserInformationJob.join()
Timber.d("After loadUserInformationFromFirestore in runBlocking")
}
Timber.d("After loadUserInformationFromFirestore after runBlocking")
}
suspend fun loadUserInformationFromFirestore(uid: String) {
try {
// Get document from Firestore
val docRef =
mFirestore!!.collection("Users").document(uid).collection("UserProfile").document(uid)
docRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val document = task.result
if (document.exists()) {
Timber.d("In LoadUserInformationFromFirestore")
} else if (!document.exists()) {
Timber.e(
"In LoadInformationFromFirestore, User document does not exist. Send user through into questions."
)
} else {
Timber.e("In LoadInformationFromFirestore, Error loading user document")
}
} else {
Timber.e(
"In LoadInformationFromFirestore, get Firestore failed with ",
task.exception
)
}
}
} catch (e: Exception) {
Timber.w("Error getting Firebase User", e)
}
}
我认为该问题与 Firestore 回调的异步性质有关。
addOnCompleteListener
内部的loadUserInformationFromFirestore
回调是异步的,runBlocking不会等待它完成。
您可以使用
suspendCoroutine
或 suspendCancellableCoroutine
将异步 Firestore 任务转换为可挂起的协程。这样,您可以确保 runBlocking 协程将等待 Firestore 任务完成后再继续。
看这里:
var mFirestore: FirebaseFirestore? = null
fun getGroupSummaries(groupInfo: GroupInformation, adminUserInfo: UserInformation) {
mFirestore = FirebaseFirestore.getInstance()
val uid = "3iHWCsGmKvfq4w9OgDNWZsSs8wy2"
runBlocking {
try {
val userInformation = loadUserInformationFromFirestore(uid)
Timber.d("After loadUserInformationFromFirestore in runBlocking")
} catch (e: Exception) {
Timber.e("Error loading user information", e)
}
}
Timber.d("After loadUserInformationFromFirestore after runBlocking")
}
suspend fun loadUserInformationFromFirestore(uid: String): UserInformation = suspendCancellableCoroutine { continuation ->
try {
// Get document from Firestore
val docRef =
mFirestore!!.collection("Users").document(uid).collection("UserProfile").document(uid)
docRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val document = task.result
if (document.exists()) {
Timber.d("In LoadUserInformationFromFirestore")
// Assuming UserInformation is a class you want to return
continuation.resume(UserInformation(/* populate with data */))
} else {
Timber.e("In LoadInformationFromFirestore, User document does not exist.")
continuation.resumeWithException(DocumentNotFoundException("User document not found"))
}
} else {
Timber.e("In LoadInformationFromFirestore, get Firestore failed with ", task.exception)
continuation.resumeWithException(task.exception ?: RuntimeException("Unknown error"))
}
}
} catch (e: Exception) {
Timber.w("Error getting Firebase User", e)
continuation.resumeWithException(e)
}
}
class DocumentNotFoundException(message: String) : RuntimeException(message)
这里,runBlocking 协程将等待 Firestore 任务完成,然后再进入下一行。此外,它还处理异常并提供更结构化的返回结果的方式。
希望有帮助!