首页 > 解决方案 > 在 Android 中使用 Kotlin Coroutines 保存文件显示不适当的阻塞方法调用

问题描述

我创建了一个将图像保存到内部文件目录的函数。我在 Android 提供的生命周期范围内执行此函数,如下所示:

override fun onCreate(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        saveImage(id, proxyEntity)
    }
}

这是我要保存图像的功能。

    private suspend fun saveImage(
            id: Long,
            proxyEntity: ProxyEntity,
        ) {
            val entitiesDirectory = File(filesDir, "local/entities")
            if (false == entitiesDirectory.isDirectory) {
                entitiesDirectory.mkdirs()
            }
            if (null != selectedImage) {
                withContext(Dispatchers.IO) {
                    val entityFile = File(entitiesDirectory, "$id")
                    val fileOutputStream = FileOutputStream(entityFile)
                    //selectedImage is of type Bitmap
                    fileOutputStream.write(ImageUtil.convertBitmapToByteArray(selectedImage!!))
                    fileOutputStream.close()
                    proxyEntity.imagePath = "local/entities/${id}"
                }
            }
        }

代码本身正在运行,但我的问题是,为什么 Android-Studio 仍然向我显示“不适当的阻塞方法调用”以及我是否做错了什么。

编辑: 消息出现在FileOutputStream(entityFile)和功能write上。close

标签: androidandroid-studiokotlinkotlin-coroutines

解决方案


更新:阅读后Tenfour04的评论

起初我很犹豫,因为从理论上讲,suspend这并不意味着任何关于上下文的事情。但就像生活中的大多数事情一样,在更多地思考它(和睡觉)之后,我认为以一种实际的方式做出一些可控一致的选择是有意义的。其中,要确保:

  1. 您可以在单元测试中替换 Dispatcher(这意味着:不要硬编码,而是注入它以便您可以替换它)。

例如:而不是:

...launch(Dispatchers.IO) { ... }

做:

class XYZ(private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default) { 
 

suspend fun xyz() = withContext(defaultDispatcher) { ... }

你明白了。

  1. 挂起函数应该可以安全地从主线程调用

这是一个有趣的选择,但我明白为什么。我将开始在我的所有代码中执行此操作。来源:挂起函数应该可以安全地从主线程调用

我建议您阅读Google 最近更新/发布的Coroutines Best Practices 。我们可能都有自己的分歧并在这里和那里使用不同的模式,但总的来说,我会按照谷歌的建议去做;最后,这是他们的平台,您越接近他们的代码,就越容易应对现代开发中不断发生的变化和弃用。

更新:我没有喝咖啡。

好的,在重新阅读您的代码后,我注意到您确实指定了协程上下文。

话虽如此,我会按照我的方式进行上下文分配(在视图模型启动的调用中)。

您不希望您的代码“关心”使用哪个上下文,而是让视图模型决定什么是最好的。

原始回复

主线程上的 I/O 操作是个坏主意(如果不是被某些 API 禁止的话),因此您需要更改调度程序

你的代码:

lifecycleScope.launch {
        saveImage(id, proxyEntity)
}

在默认调度程序(可能是主线程)上运行。

尝试改为使用 I/O 之一:

lifecyleScope.launch(Dispatchers.IO) {
   saveImage(id, proxyEntity)
}

推荐阅读