【问题标题】:How to properly use firebase-firestore async calls如何正确使用 firebase-firestore 异步调用
【发布时间】:2022-06-27 21:23:27
【问题描述】:

我正在做我的最后一年项目,我真的很难决定是否应该使用 Kotlin 的回调或协程。我为 firebase 创建了单独的模块,它的所有操作都在那里完成,以适应它的数据检索或任何其他功能。

问题是,每当我从函数返回用户时,由于异步调用,它返回 null,比我理解的要多,之后我像这样使用回调:

fun getUserAsModel(callback: (User) -> Unit) {
    FirebaseAuth.getInstance().uid?.let {
        firestore.collection(Constants.FireCollections.USERS)
            .document(it)
            .get()
            .addOnSuccessListener { it1 ->
                val user = it1.toObject(User::class.java)?.let { it2 ->
                    callback(it2)
                }
            }
            .addOnFailureListener {
                Log.e(TAG, "In userModel()->", it)
                it.stackTrace
            }
    }
}

但是我在很多形式中看到我应该使用协程,现在我正在使用这种方法但它不起作用:

fun getUser () : User? {
    var user:User? = null
    val collection = firestore.collection(Constants.FireCollections.USERS)
    val document = collection.document(FirebaseAuthRepository().getCurrentUserId())
    try {
        scope.launch {
            val snapshot = document.get().await()
            user = snapshot.toObject(User::class.java)
        }
    } catch (e:FirebaseFirestoreException) {
        Log.e(TAG, "In getUser() -> " ,e)
        e.stackTrace
    }
    return user
}

我仍然卡住了,因为每次我使用 getUser() 时我都需要启动协程的作用域,这真的让代码变得很垃圾。

我想了解您的解决方案,我应该如何正确实施。谢谢

【问题讨论】:

  • 说实话真的没关系,如果你已经完成并希望改进你的整体代码,那么你可以尝试不同的东西,当然,但不要浪费时间在“改进”上已经有效的代码。你对回调方法有什么问题吗?如果没有,就使用它,把时间花在改进项目上,而不是担心小细节
  • getUser 应该完全是一个挂起函数,以便它像你想要的那样工作,此时它返回 null 似乎是有道理的
  • @a_local_nobody 感谢您的及时回复。我担心的原因是异步调用真的很慢,我的主管一直在告诉我为什么它很慢。主要原因是当我的应用程序按要求启动时,我需要在用户处理屏幕之前检查用户在 Firestore 中的电话号码,所以这里的呼叫真的很慢才能让用户检查号码。
  • 不幸的是,缓慢的 api 调用只是工作的一部分 :) 您也许可以调查本地缓存数据,或者只是确保您的应用程序中有适当的加载指示器来显示正在发生的事情,但是没有'你可以做很多事情来改善它,协程和回调都不会真正产生足够的显着影响来改善这一点
  • 我认为article 可能会帮助您了解从 Firestore 读取数据的几种方式之间的区别。

标签: android firebase kotlin google-cloud-firestore kotlin-coroutines


【解决方案1】:

您正在重现与异步调用相同的问题,因为协程是异步启动的。使用协程的正确方法是使其成为挂起函数并直接返回用户,而无需在该函数内启动另一个协程。

函数应该如下所示:

suspend fun getUser () : User? {
    val collection = firestore.collection(Constants.FireCollections.USERS)
    val document = collection.document(FirebaseAuthRepository().getCurrentUserId())
    return try { 
        val snapshot = document.get().await()
        snapshot.toObject(User::class.java)
    } catch (e: FirebaseFirestoreException) {
        Log.e(TAG, "In getUser() -> ", e)
        null
    }
}

回调与协程是一个偏好问题。协程学习起来并非易事,但一旦你学会了,你的代码就会看起来更干净,更容易理解。

【讨论】:

  • 请记住,这意味着您必须在其他地方启动协程才能调用此函数,并且您必须在该协程中使用结果。
  • 我创建了这个 -> val scope = CoroutineScope(CoroutineName("LoginFragment")) 然后调用了挂起函数。还有什么方法可以使这个调用同步而不是异步,以便快速返回值?
  • 不,您无法让 Firebase 更快地获取值。否则他们会很快开始。这是一个 IO 操作,所以它总是不能安全地在主线程上同步调用。此外,您可能不应该创建自己的协程范围。使用预先存在的lifecycleScope 或viewModelScope,这样当相关组件的生命周期结束时,您的协程可以自动取消。
  • 我不使用 Firebase,所以我不确定,但也许您可以缓存用户并将相关数据保存到磁盘并在会话仍然有效时将其恢复以更快地加载它若有可能。这仍然是一个 IO 操作,但从文件读取通常比从 Internet 获取更快。
  • 协程不是你可以在不理解的情况下试验和使用的东西。在使用它们之前,您需要通读文档以完全理解它们。您可以在 Android 文档中了解如何使用 lifecycleScope 和 viewModelScope。由于这是从 Firebase 获取的,因此您几乎可以肯定应该在 ViewModel 中执行此操作并使用viewModelScope,而不是lifecycleScope,如果您的设计是干净的。
【解决方案2】:

你也可以使用callbackflow

fun getUserAsModel():Flow<User?> {
        return callbackFlow {
            FirebaseAuth.getInstance().uid?.let {
                firestore.collection(Constants.FireCollections.USERS)
                    .document(it)
                    .get()
                    .addOnSuccessListener { it1 ->
                        val user = it1.toObject(User::class.java)
                        trySend(user)
                        
                    }
                    .addOnFailureListener {
                        Log.e(TAG, "In userModel()->", it)
                        it.stackTrace
                        cancel(it)
                    }
                awaitClose { close() }
            }
        }

    }

【讨论】:

    猜你喜欢
    • 2021-11-05
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    相关资源
    最近更新 更多