【问题标题】:How to use Firestore databse : addSnapshotListener using await() in Kotlin?如何使用 Firestore 数据库:在 Kotlin 中使用 await() addSnapshotListener?
【发布时间】:2021-06-26 17:52:22
【问题描述】:

我有一个 DAO 类,其中我有 fetchHubList 方法,该方法使用 await() 从云 Firestore 异步获取文档集合。这个实现使用了我后来知道的“get()”方法,它不会获取实时更新。在尝试使用 onSnapshotListener 类似地实现代码时会出现错误(这是很诚实的,因为 get() 和此方法返回完全不同的东西)。有谁知道如何实现这个?

代码目前如何:

 suspend fun fetchHubList(): ArrayList<HubModel>? = try {
        val hubList = ArrayList<HubModel>()

        hubsListCollection.get().await().map { document ->

            if (document != null) {

                Log.d(TAG, "Data fetch successful!")
                Log.d(TAG, "the document id is ${document.id}")

                val temp = HubModel(document.get("hubName").toString(),
                                    document.id.toString(),
                        document.get("isAdmin") as Boolean)
                hubList.add(temp)

                     //   hubList.add(document.toObject(HubModel::class.java))

            } else {
                Log.d(TAG, "No such document")
            }

        }

我想在这里实现的(这是完全错误的):

suspend fun fetchHubList(): ArrayList<HubModel>? = try {
        val hubList = ArrayList<HubModel>()

        hubsListCollection.addSnapshotListener().await().map { document ->

            if (document != null) {

                Log.d(TAG, "Data fetch successful!")
                Log.d(TAG, "the document id is ${document.id}")

                val temp = HubModel(document.get("hubName").toString(),
                                    document.id.toString(),
                        document.get("isAdmin") as Boolean)
                hubList.add(temp)

                     //   hubList.add(document.toObject(HubModel::class.java))

            } else {
                Log.d(TAG, "No such document")
            }

        }

我在我的 ViewModel 类中使用这个函数来创建一个 LiveData 包装的 ArrayList:

val hubList =  MutableLiveData<ArrayList<HubModel>>()
    private val hubListDao = HubListDao()
    init {
        viewModelScope.launch {

            hubList.value = hubListDao.fetchHubList()
        }
    }

提前致谢!

【问题讨论】:

  • 一个快照监听器可以触发多次,而await() 只等待一个结果。两种范式不匹配。为什么要使用快照侦听器?
  • 我想使用 smapshot 监听器来监听我的数据库中可能发生的变化以更新我的回收站视图
  • 在这种情况下,您不能 await() 来自数据库的结果,因为 await 只需要一次数据。我不是MutableLiveData 方面的专家,但看来您可能正在寻找postValuestackoverflow.com/questions/44204978/…
  • @FrankvanPuffelen 我认为 postValue 在这里没有多大帮助。我实际上正在寻找的是一种异步执行快照侦听的方法。如果我不能使用等待,那么我是否必须使用 GloabalScope(Dispatchers.IO) 之类的东西?那么是的,我猜后值会有所帮助
  • 只是好奇。我应该使用 kotlin Flows 吗?

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


【解决方案1】:

您不需要addSnapshotListener,只需使用get

hubsListCollection.get().await()

为了观察你的收藏的变化,你可以扩展LiveData

class CafeLiveData(
    private val documentReference: DocumentReference
) : LiveData<Cafe>(), EventListener<DocumentSnapshot> {

    private var snapshotListener: ListenerRegistration? = null

    override fun onActive() {
        super.onActive()
        snapshotListener = documentReference.addSnapshotListener(this)
    }

    override fun onInactive() {
        super.onInactive()
        snapshotListener?.remove()
    }

    override fun onEvent(result: DocumentSnapshot?, error: FirebaseFirestoreException?) {
        val item = result?.let { document ->
            document.toObject(Cafe::class.java)
        }
        value = item!!
    }
}

并从您的视图模型中公开它:

fun getCafe(id: String): LiveData<Cafe> {
    val query = Firebase.firestore.document("cafe/$id")
    return CafeLiveData(query)
}

【讨论】:

  • 这种方法的问题是,如果我的数据库发生更改,那么我必须重新启动我的活动以便加载更改。我希望在数据库发生更改后立即更新回收站视图。
  • @ProfessorofStupidity 我添加了一个自定义 LiveData 实现的示例来观察变化。
【解决方案2】:

正如@FrankvanPuffelen 在他的评论中已经提到的那样,您无法将“.await()”与“addSnapshotListener()”一起使用,因为两者是两个完全不同的概念。一个用于只获取一次数据,而第二个用于收听实时更新。这意味着您可以从您正在收听的参考中接收到连续的数据流。

请注意,“.await()”在 Kotlin 中用于挂起函数。这意味着当您调用“.await()”时,您会启动一个单独的协程,这是一个不同的线程,可以在需要时与其他协程并行工作。这称为异步编程,因为“.await()”启动协程执行并等待其完成。换句话说,如果没有抛出异常,您可以在延迟值上使用“.await()”来获取其最终结果。不幸的是,这种机制不适用于实时更新。

对于 Firestore,您可以在 DocumentReference 对象、Query 对象或 CollectionReference 对象上调用“.await()”,这实际上是一个没有过滤器的查询。这意味着您正在等待结果/结果可用。因此,您可以从此类调用中获取一个文档或多个文档。但是,以下调用:

hubsListCollection.addSnapshotListener().await()

不起作用,因为“addSnapshotListener()”方法返回一个ListenerRegistration 对象。

我想使用快照侦听器来侦听数据库中可能发生的更改以更新我的 RecyclerView

在这种情况下,您应该考虑使用名为 Firebase-UI for Android 的库。在这种情况下,所有繁重的工作都将在幕后完成。所以不需要任何协程或“.await()”调用,一切都是实时同步的。

如果您既不想使用 Kotlin 协程,也不想使用 Firebase-UI 库,则可以使用 LiveData。在我的以下 repo 中可以看到一个具体的例子:

您可以在其中继承 LiveData 类并实现 EventListener 接口。

【讨论】:

  • 谢谢!但我试图避免使用 Firebase UI,因为我正在尝试使用 MVVM 架构实现我的应用程序并且它们不能一起使用(您的答案只有,XD stackoverflow.com/q/66704121/12270082)。最近,我正在阅读文档中的 Flows,它是专门为从数据库获取实时更新等任务而设计的。我应该在我的 fetchHubList 方法中使用它吗?
  • 是的,它是其中之一。如果你想继续使用 MVVM,那么库的功能应该自己实现。关于 Flow,here 是一个很好的资源。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-05
相关资源
最近更新 更多