我需要从图库中获取一些图像,处理它们(调整大小,压缩...)并将它们保存到某个路径。但是,我需要对呼叫进行排队,因为旧设备无法同时处理多个图像。
我正在使用Glide,这是用于处理一个图像的代码:
fun processImage(context: Context, sourcePath: String, destinationPath: String, quality: Int, width: Int, height: Int, deleteOriginal: Boolean, callback: ((success: Boolean) -> Unit)) {
val sourceFile = File(sourcePath)
val destinationFile = File(destinationPath)
GlideApp.with(context)
.asBitmap()
.load(sourceFile)
.into(object : SimpleTarget<Bitmap>(width, height) {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
try {
destinationFile.writeBytes(ImageUtilities.imageToByteArray(resource, quality, Bitmap.CompressFormat.JPEG, false))
if (deleteOriginal) {
val originalFile = File(sourcePath)
originalFile.delete()
}
callback.invoke(true)
} catch (ex: Exception) {
callback.invoke(false)
}
}
})
}
现在我通过调用processNextImage
手动排队调用,private fun processImages(sourceImagePaths: List<String>) {
processNextImage(sourceImagePaths, 0)
}
private fun processNextImage(sourceImagePaths: List<String>, index: Int) {
val imagePath = sourceImagePaths[index]
val destination = FileUtilities.generateImagePath()
processImage(this, imagePath, destination, 90, 1000, 1000, false) {
processedImagePaths.add(destination)
if (index + 1 < sourceImagePaths.count())
processImage(sourceImagePaths, index + 1)
else
success()
}
}
递归调用自身,直到处理完所有图像:
onResourceReady
但是我不认为这是最好的方法,我试着查看Kotlin协程,但我发现的只是排队代码已经阻塞的例子,这不符合我的情况因为Glide已经处理异步调整大小并在回调{{1}}
中返回结果干净的方法有什么想法吗?
答案 0 :(得分:0)
我能够按照Marko comment中的建议使用suspendCoroutine
来解决问题,这是我的代码:
private fun processImages(sourceImagePaths: List<String>) {
async(UI) {
sourceImagePaths.forEach { path ->
processNextImage(path)?.let {
processedImagePaths.add(it)
}
}
if (processedImagePaths.isEmpty()) finishWithFailure() else finishWithSuccess()
}
}
private suspend fun processNextImage(sourceImagePath: String): String? = suspendCoroutine { cont ->
val destination = FileUtilities.generateImagePath()
processImage(this, sourceImagePath, destination, 90, 1000, 1000, false) { success ->
if (success)
cont.resume(destination)
else
cont.resume(null)
}
}
方法processImages
遍历路径列表,并为每个路径调用processNextImage
。由于processNextImage
包含suspendCoroutine
,因此它将阻止线程直到cont.resume
被调用,这可以保证在完成当前图像之前不会处理下一个图像。
答案 1 :(得分:0)
如the official documentation中所述,如果您想基于可挂起函数将基于回调的API转换为一个API,则需要遵循一个简单的模式。我在这里解释一下这个描述。
您的关键工具是名为suspendCoroutine()
的标准库中的函数。假设您有someLongComputation
函数,其回调函数接收Result
对象:
fun someLongComputation(params: Params, callback: (Result) -> Unit)
您可以使用以下简单代码将其转换为暂停功能:
suspend fun someLongComputation(params: Params): Result =
suspendCoroutine { cont ->
someLongComputation(params) { cont.resume(it) }
}
注意传递给原始回调的对象的类型如何变成了可挂起函数的返回值。
有了这个,你可以看到coroutines的魔力发生在你面前:即使它看起来像一个阻塞的呼叫,但事实并非如此。协程将在幕后暂停,并在返回值准备好后恢复 - 以及它将如何恢复完全由您控制。