首页 > 解决方案 > Firebase addOnSuccessListener 回调运行上下文抛出`NetworkOnMainThreadException`

问题描述

假设我有一个存储库类,它定义了一个从 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)
                    }
                }
            }
        }
    }

我有一些问题:

标签: androidmultithreadingkotlinfirebase-storagecoroutine

解决方案


存储库类中 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


推荐阅读