使用回调对多个异步调用进行排队

时间:2018-02-21 13:01:31

标签: android kotlin

我需要从图库中获取一些图像,处理它们(调整大小,压缩...)并将它们保存到某个路径。但是,我需要对呼叫进行排队,因为旧设备无法同时处理多个图像。

我正在使用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}}

中返回结果

干净的方法有什么想法吗?

2 个答案:

答案 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的魔力发生在你面前:即使它看起来像一个阻塞的呼叫,但事实并非如此。协程将在幕后暂停,并在返回值准备好后恢复 - 以及它将如何恢复完全由您控制。