【问题标题】:Firebase addOnSuccessListener callback running context throws `NetworkOnMainThreadException`Firebase addOnSuccessListener 回调运行上下文抛出`NetworkOnMainThreadException`
【发布时间】:2020-09-17 09:24:34
【问题描述】:

假设我有一个存储库类,它定义了一个从 Firebase 存储中检索图像的方法,并定义了一个成功侦听器,该侦听器调用从视图模型定义的回调

fun getAdImg(imgId: String, callback: (stream: Bitmap?) -> Unit) {
        storage.child(FOLDER).child(imgId+EXTENSION).stream
            .addOnSuccessListener {
                callback(BitmapFactory.decodeStream(it.stream))
                Log.i(TAG, "getAdImg success")
            }
            .addOnCanceledListener {
                Log.e(TAG, "getAdImg canceled")
            }
            .addOnFailureListener {
                Log.e(TAG, "getAdImg failure")
            }

    }

视图模型中定义回调并调用存储库的函数如下

fun loadImage(imgId: String?) {
        imgId?.let { id ->
            if (_img.value == null) {
                viewModelScope.launch (context = IO) {
                    AdvertisementRepository.getInstance().getAdImg(id) { bitmap ->
                        _img.postValue(bitmap)
                    }
                }
            }
        }
    }

我有几个问题:

  • 存储库类中addOnSuccessListener定义的监听器的生命周期是什么?
  • 在侦听器中调用callback 函数的范围是什么?
  • 当我尝试运行此代码时,BitmapFactory.decodeStream(it.stream) 将抛出一个 android.os.NetworkOnMainThreadException,由于调用 decodeStream 函数的上下文,这似乎是一个异常

【问题讨论】:

    标签: android multithreading kotlin firebase-storage coroutine


    【解决方案1】:

    repository 类中 addOnSuccessListener 定义的监听器的生命周期是什么?

    您在这里使用它的方式,它没有生命周期。回调将无限期持续。

    在监听器中调用回调函数的作用域是什么?

    它没有协程范围。只要结果准备好,就会在主线程上调用回调。

    当我尝试运行此代码时,BitmapFactory.decodeStream(it.stream) 将抛出一个 android.os.NetworkOnMainThreadException,由于调用 decodeStream 函数的上下文,这似乎是一个异常

    是的,由于回调是在主线程上调用的,并且 decodeStream 进行 I/O,所以如果启用严格模式来检测主线程上的 I/O,您会期望它会抛出异常。这就是为什么 getStream() 的 API 文档说:

    通过 InputStream 异步下载此 StorageReference 处的对象。 应在通过 addOnSuccessListener(Executor, OnSuccessListener) 注册以在后台线程上运行的 OnSuccessListener 上读取 InputStream

    文档建议您安排在使用 Executor 的主线程以外的线程上调用回调。或者,您可以使用协程,但 API 不支持 Kotlin,因此您需要自行安排。有一个库可以帮助将任务转换为协程使用。

    https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-play-services

    【讨论】:

    • 假设我有一个存储库类,其方法从不同片段中的多个视图模型调用,并且存储库的每个方法使用 addOnSuccessListener 执行异步 Firebase 请求来处理每个请求。这样做会产生大量无限期运行的听众吗?如果是这样,我该如何处理?我可以在存储库中传递活动实例并为它们定义一个生命周期,但是如果我将使用该存储库执行许多请求会发生什么?
    • 如果您有新问题,请单独发布,并说明哪些问题没有按照您的预期工作。
    猜你喜欢
    • 1970-01-01
    • 2021-07-27
    • 1970-01-01
    • 1970-01-01
    • 2016-12-11
    • 1970-01-01
    • 2014-09-08
    • 2012-04-11
    • 2016-09-20
    相关资源
    最近更新 更多