如何使用 kotlin 协程进行 Firestore 查询

mr.*_*oot 5 android kotlin firebase google-cloud-firestore kotlin-coroutines

Kotlin我用和创建了一个应用程序Firebase Firestore。现在我需要实现协程,因为主线程上有很多工作。但我也是一个初学者,所以这对我来说是新的东西。我看过一些关于此的教程,但没有找到完整的教程Firestore协程的完整教程。所以我需要一些帮助来在我的应用程序中实现协程,在这样的部分(我自己尝试过,但没有得到它)​​。

从 Firestore 检索帖子。

private fun retrievePosts() {
             FirebaseFirestore.getInstance().collection("Posts")
            .orderBy("timeStamp", Query.Direction.DESCENDING)
            .get()
            .addOnSuccessListener { queryDocumentSnapshots ->
                postList?.clear()
                for (documentSnapshot in queryDocumentSnapshots) {
                    val post = documentSnapshot.toObject(Post::class.java)
                    postList?.add(post)
                }
                postAdapter?.notifyDataSetChanged()
                postAdapter?.setOnPostClickListener(this)
                if (isRefreshed) {
                    swipe_refresh_home?.setRefreshing(false)
                    isRefreshed = false
                }
                swipe_refresh_home?.visibility = VISIBLE
                progress_bar_home?.visibility = GONE
            }.addOnFailureListener { e ->
                Log.d(TAG, "UserAdapter-retrieveUsers: ", e)
                swipe_refresh_home?.visibility = VISIBLE
                progress_bar_home?.visibility = GONE
            }
    }
Run Code Online (Sandbox Code Playgroud)

将用户数据放入适配器

private fun userInfo( fullName: TextView, profileImage: CircleImageView,
                      about: TextView, uid: String,
                      userLocation: TextView, itemRoot: LinearLayout ) {

        val userRef = FirebaseFirestore.getInstance().collection("Users").document(uid)
        userRef.get()
                .addOnSuccessListener {
                    if (it != null && it.exists()) {
                        val user = it.toObject(User::class.java)
                        Glide.with(mContext).load(user?.getImage()).placeholder(R.drawable.default_pro_pic).into(profileImage)

                        fullName.text = user?.getFullName().toString()

                        about.text = user?.getAbout()

                        if (user?.getLocation() != ""){
                            userLocation.visibility = VISIBLE
                            userLocation.text = user?.getLocation()
                        }

                        if (profileImage.drawable == null){
                            itemRoot.visibility = GONE
                        }
                        else{
                            itemRoot.visibility = VISIBLE
                        }
                    }
                }
    }
Run Code Online (Sandbox Code Playgroud)

以及适配器中的“保存帖子”按钮。

private fun savedPost(postId: String, saveButton: ImageView?) {
        FirebaseFirestore.getInstance().collection("Users").document(currentUserID)
                .collection("Saved Posts").document(postId)
                .get()
                .addOnSuccessListener {
                    if (it.exists()) {
                        saveButton?.setImageResource(drawable.ic_bookmark)
                    } else {
                        saveButton?.setImageResource(drawable.bookmark_post_ic)
                    }
                }
    }
Run Code Online (Sandbox Code Playgroud)

Ale*_*amo 8

正如我看到您的代码,您正在使用以下查询:

val queryPostsByTimestamp = FirebaseFirestore.getInstance().collection("Posts")
        .orderBy("timeStamp", Query.Direction.DESCENDING)
Run Code Online (Sandbox Code Playgroud)

最有可能从“Posts”集合中获取 Post 对象的列表。

为了使用Kotlin Coroutines,不要忘记在 Gradle (app) 文件中添加以下依赖项:

 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.9"
 implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
 implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
Run Code Online (Sandbox Code Playgroud)

我将为您提供使用 MVVM 架构模式的解决方案。因此,我们将使用存储库类和 ViewModel 类。对于对 Firestore 的异步调用,我们将使用Flow

对于从数据库调用中获得的响应,我们需要一个如下所示的密封类:

sealed class Response<out T> {
    class Loading<out T>: Response<T>()

    data class Success<out T>(
        val data: T
    ): Response<T>()

    data class Failure<out T>(
        val errorMessage: String
    ): Response<T>()
}
Run Code Online (Sandbox Code Playgroud)

假设您有一个“Post”类,让我们在存储库类中创建以下函数:

fun getPostsFromFirestore() = flow {
    emit(Loading())
    emit(Success(queryPostsByTimestamp.get().await().documents.mapNotNull { doc ->
        doc.toObject(Post::class.java)
    }))
}. catch { error ->
    error.message?.let { errorMessage ->
        emit(Failure(errorMessage))
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我们将根据状态发出一个对象。第一次调用该函数时,我们使用 发出加载状态emit(Loading(),当我们获取数据时,我们发出List<Post>,如果出现错误,我们使用 发出错误消息 Failure(errorMessage)

现在我们需要从 ViewModel 类调用此函数:

fun getPosts() = liveData(Dispatchers.IO) {
    repository.getPostsFromFirestore().collect { response ->
        emit(response)
    }
}
Run Code Online (Sandbox Code Playgroud)

通过上述函数,我们收集从 getPostsFromFirestore() 函数调用获得的数据,并将结果作为 LiveData 对象进一步发出,以便可以在活动/片段中观察到它,如下所示:

private fun getPosts() {
    viewModel.getPosts().observe(this, { response ->
        when(response) {
            is Loading -> //Load a ProgessBar
            is Success -> {
                val postList = response.data
                //Do what you need to do with your list
                //Hide the ProgessBar
            }
            is Failure -> {
                print(response.errorMessage)
                //Hide the ProgessBar
            }
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

差不多就这些了!


bro*_*oot 0

我不了解 Firebase,所以我可能会错过一些东西,但一般来说,您不需要库中的特殊支持即可将其与协程一起使用。如果您启动一个后台协程,然后在其中执行上述代码,那么 Firebase 可能会在您的协程中运行而不会出现任何问题。

唯一有问题的部分可能是听众。有些库在用于执行它们的线程中调用回调,但有些库将回调分派到特定线程。对于 Firebase,默认情况下它似乎在主线程中运行侦听器。如果这不是您想要的,您也可以传递一个执行器来在协程中运行回调,例如:

.addOnSuccessListener(Dispatchers.Default.asExecutor()) { ... }
Run Code Online (Sandbox Code Playgroud)

  • Firebase 完全支持协程。它具有所有回调的挂起函数替代方案。 (2认同)