在 Android 中使用 Kotlin Coroutines 保存文件显示不适当的阻塞方法调用

Save file with Kotlin Coroutines in Android shows inappropiate blocking method call

我创建了一个将图像保存到内部文件目录的功能。我在 Android 提供的 lifecycleScope 中执行此函数,如下所示:

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) 以及 writeclose 函数上。

更新:阅读 Tenfour04 的评论后

起初我很犹豫,因为理论上,suspend并没有暗示任何关于上下文的信息。但是就像生活中的大多数事情一样,在更多地思考(和睡觉)之后,我认为以一种实用的方式使一些 controlledconsistent[=57 是有意义的=] 选择。其中,保证:

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

例如: 而不是:

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

做:

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

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

你懂的。

  1. 挂起函数从主线程调用应该是安全的

这是一个有趣的选择,但我明白为什么。我将尽可能在我的所有代码中开始这样做。 资料来源:Suspend functions should be safe to call from the main thread.

我建议您最近 updated/published 阅读 Google 的 Coroutines Best Practices。我们可能都有不同的意见,并在这里和那里使用不同的模式,但总的来说,我会按照 Google 的建议去做;最后,这是他们的平台,你越接近他们的代码,就越容易处理当今现代开发中不断发生的变化和弃用。

更新:我还没喝咖啡。

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

也就是说,我会按照我的方式进行上下文分配(在视图模型启动的调用中)。

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

原始回复

主线程上的I/O操作是个坏主意(如果不是被某些API禁止的话),所以你需要改变调度器

您的代码:

lifecycleScope.launch {
        saveImage(id, proxyEntity)
}

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

尝试改用 I/O 一个:

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