【问题标题】:Firebase And Kotlin CoroutinesFirebase 和 Kotlin 协程
【发布时间】:2019-12-25 12:39:24
【问题描述】:

我有这个静态(伴随对象)函数来从 Firebase Firestore 下载事件信息和从 Firebase Storage 下载事件图像:

fun downloadEventInformationAndImages() {
    FirebaseFirestore.getInstance().collection("events").document(downloadedEventID)
    .get().addOnSuccessListener { snap ->

    //Download Event Information Here
    //Do stuff

    GlobalScope.launch(Dispatchers.Main) {
        //Download Event Images Here
        val downloadEventImage = FirebaseStorage.getInstance().reference.child("Images/Events/$eventID/eventPhoto.jpeg")
                            .getBytes(1024 * 1024).asDeferred()
        val downloadEventFounderImage = FirebaseStorage.getInstance().reference.child("Images/Users/$founderID/profilePhoto.jpeg")
                            .getBytes(1024 * 1024).asDeferred()

        try {
           val downloadedImages = mutableListOf<ByteArray>(
                                downloadEventImage.await(),
                                downloadEventFounderImage.await())
            // Update stuff on UI
        } catch(e: StorageException) {
            // Error handling
        }
     }.addOnFailureListener { exception ->
     // Error handling
     }
    }
}

我想要做的是避免使用 GlobalScope,但是当我尝试将 runBlocking 添加到 downloadEventInformationAndImages() 时:

fun downloadEventInformationAndImages() = runBlocking {
    // Do stuff
    launch(Dispatchers.Main) {

它不起作用(它没有等待 Firebase 完成下载 - 然后我将 runBlocking 移到函数内部,也不起作用)。如何避免使用 GlobalScope?提前致谢。

【问题讨论】:

  • 你不能只传递与调用者生命周期相关的CoroutineScope 作为downloadEventInformationAndImages 的参数吗?
  • 嘿@RicardoCosteira!它有效 - 我很惊讶答案如此简单 - 谢谢! :) 由于我是 Kotlin 和 Coroutines 的新手,在传递 CoroutineScope 作为参数(例如内存泄漏等)时我应该注意哪些具体事项?
  • 很高兴它成功了 :) 我会在几分钟内写出更详细的答案。

标签: firebase kotlin google-cloud-firestore firebase-storage


【解决方案1】:

我在这里看到的最佳选择是将CoroutineScope 作为downloadEventInformationAndImages 的参数传递。那就是

fun downloadEventInformationAndImages(scope: CoroutineScope) {
  FirebaseFirestore.getInstance().collection("events").document(downloadedEventID)
  .get().addOnSuccessListener { snap ->

  //Download Event Information Here
  //Do stuff

    scope.launch(Dispatchers.Main) { ... }
  }
}

你需要注意的一件事是你在这里启动的每个协程现在都在你传入的范围内启动,这意味着如果它失败或被取消,它也会取消任何父协程。要了解如何处理此问题,您应该查看Jobs 的文档。另一方面,您也可以使用SupervisorJob(在上面的文档链接中提到)构建您的CoroutineScope,其中子协程失败而不影响父协程。最后,当拥有 CoroutineScope 的对象到达其生命周期的末尾时,清理您的 CoroutineScope 也是一个好习惯。这将避免可能的内存泄漏。可以使用scope.cancel()scope.coroutineContext.cancelChildren() 进行清理。第一个终止范围的作业(传播到所有子作业),第二个只是取消可能存在的任何子作业。我建议您花一些时间阅读有关协程的文章甚至文档,因为其中有很多细微差别:)

【讨论】:

    【解决方案2】:

    你可以把你的函数写成CoroutineScope扩展函数:

    fun CoroutineScope.downloadEventInformationAndImages() {
        ...
        launch(Dispatchers.Main) {
        ...
    }
    

    并从 ViewModel 或其他有范围的地方调用它:

    uiScope.downloadEventInformationAndImages()
    

    【讨论】:

    • 你好安德烈!非常感谢您的回答。由于我是 Kotlin 和 Coroutines 的新手,因此我无法实现您的答案(我应该的方式 - 我认为)。但是里卡多在第一篇文章的评论中提出的方式对我有用。再次感谢您的回答。
    • 你好,Metin。没问题 :) 扩展是非常有用的 Kotlin 功能。如果你想深入了解 Kotlin,总有一天你会读到这篇文章 kotlinlang.org/docs/reference/extensions.html
    猜你喜欢
    • 2020-12-29
    • 2019-08-03
    • 2019-02-14
    • 2022-08-18
    • 2021-12-08
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    相关资源
    最近更新 更多