在 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)
以及 write
和 close
函数上。
更新:阅读 Tenfour04
的评论后
起初我很犹豫,因为理论上,suspend
并没有暗示任何关于上下文的信息。但是就像生活中的大多数事情一样,在更多地思考(和睡觉)之后,我认为以一种实用的方式使一些 controlled 和 consistent[=57 是有意义的=] 选择。其中,保证:
- 您可以在单元测试中替换 Dispatcher(这意味着:不要硬编码,而是注入它以便您可以替换它)。
例如:
而不是:
...launch(Dispatchers.IO) { ... }
做:
class XYZ(private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default) {
suspend fun xyz() = withContext(defaultDispatcher) { ... }
你懂的。
- 挂起函数从主线程调用应该是安全的
这是一个有趣的选择,但我明白为什么。我将尽可能在我的所有代码中开始这样做。
资料来源: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)
}
我创建了一个将图像保存到内部文件目录的功能。我在 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)
以及 write
和 close
函数上。
更新:阅读 Tenfour04
的评论后
起初我很犹豫,因为理论上,suspend
并没有暗示任何关于上下文的信息。但是就像生活中的大多数事情一样,在更多地思考(和睡觉)之后,我认为以一种实用的方式使一些 controlled 和 consistent[=57 是有意义的=] 选择。其中,保证:
- 您可以在单元测试中替换 Dispatcher(这意味着:不要硬编码,而是注入它以便您可以替换它)。
例如: 而不是:
...launch(Dispatchers.IO) { ... }
做:
class XYZ(private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default) {
suspend fun xyz() = withContext(defaultDispatcher) { ... }
你懂的。
- 挂起函数从主线程调用应该是安全的
这是一个有趣的选择,但我明白为什么。我将尽可能在我的所有代码中开始这样做。 资料来源: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)
}