android - 在 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
解决方案
更新:阅读后Tenfour04
的评论
起初我很犹豫,因为从理论上讲,suspend
这并不意味着任何关于上下文的事情。但就像生活中的大多数事情一样,在更多地思考它(和睡觉)之后,我认为以一种实际的方式做出一些可控和一致的选择是有意义的。其中,要确保:
- 您可以在单元测试中替换 Dispatcher(这意味着:不要硬编码,而是注入它以便您可以替换它)。
例如:而不是:
...launch(Dispatchers.IO) { ... }
做:
class XYZ(private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default) {
suspend fun xyz() = withContext(defaultDispatcher) { ... }
你明白了。
- 挂起函数应该可以安全地从主线程调用
这是一个有趣的选择,但我明白为什么。我将开始在我的所有代码中执行此操作。来源:挂起函数应该可以安全地从主线程调用。
我建议您阅读Google 最近更新/发布的Coroutines Best Practices 。我们可能都有自己的分歧并在这里和那里使用不同的模式,但总的来说,我会按照谷歌的建议去做;最后,这是他们的平台,您越接近他们的代码,就越容易应对现代开发中不断发生的变化和弃用。
更新:我没有喝咖啡。
好的,在重新阅读您的代码后,我注意到您确实指定了协程上下文。
话虽如此,我会按照我的方式进行上下文分配(在视图模型启动的调用中)。
您不希望您的代码“关心”使用哪个上下文,而是让视图模型决定什么是最好的。
原始回复
主线程上的 I/O 操作是个坏主意(如果不是被某些 API 禁止的话),因此您需要更改调度程序
你的代码:
lifecycleScope.launch {
saveImage(id, proxyEntity)
}
在默认调度程序(可能是主线程)上运行。
尝试改为使用 I/O 之一:
lifecyleScope.launch(Dispatchers.IO) {
saveImage(id, proxyEntity)
}
推荐阅读
- wordpress - Wordpress 重力表单根据选定的“国家”更新“城市”下拉列表
- java - Java - 接口方法命名约定
- uitableview - Swift:创建自定义 UITableviewCell 而不定义 IBOutlet 变量
- android - 测试运行不正常 - Android
- mysql - 在 MySQL 中减去 UK 格式的日期
- google-cloud-speech - 谷歌语音 API 错误 [32] 使用自己的 API 密钥时管道损坏
- docker - docker asp.net 核心容器在 mysql 容器之后启动
- mysql - 在 MySQL 中添加类似 JSON 的字符串
- json - 使用 circe 解码 JSON 对象时捕获未使用的字段
- java - Maven testNg 项目构建成功但未在命令提示符下运行