【问题标题】:Kotlin Coroutines Run blocking not working as expected and Cannot block execution of for loopsKotlin Coroutines Run blocking not working as expected and Cannot block execution of for 循环
【发布时间】:2020-01-21 04:49:53
【问题描述】:

在我的应用程序中,我正在执行两个 for 循环,但是这些 for 循环需要按顺序安排,这是用例:

有两个 for 循环: 1- ImageStickerslist 2-TextStickerslist

我想要做的是在 imagestickerslist 之后,如果正确完成,那么 textstickerslist 将被执行。

这里的 imagesticker 列表包含用于从 glide 加载图像的 url 路径,但是如果这些图像是高分辨率的,即使图像尚未从 url 加载,它最终也会使线程继续。为了解决这个问题,尝试添加阻塞调用以在就绪和完整方法上滑行,但它不会被证明有任何帮助。我很困惑阻塞呼叫是如何工作的,任何对此的帮助将不胜感激。

这是我执行 for 循环的代码:

 runBlocking {
                            launch {
                                imagestickers.forEach {
                                    runBlocking {
                                        var image = it.path
                                        var x = it.x
                                        var y = it.y
                                        image!!.log()
                                        setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
                                    }
                                }
                            }.join()
                            textstickers.forEach {
                                runBlocking {
                                    var text = it.text.toString()
                                    var color = it.color
                                    var font = it.font
                                    var size = it.size
                                    var x = it.x
                                    var y = it.y
                                    setTextSticker(text, Color.parseColor(color), size!!, x!!, y!!)
                                }
                            }
                        }

这是我进行主要计算的两种方法:

 fun setimagestickers(path:String,x:Int,y:Int,w:Int,h:Int){

           Glide.with(this@NewStickerActivity).asBitmap().timeout(6000000).load(path).into(object : CustomTarget<Bitmap>() {
               override fun onLoadCleared(placeholder: Drawable?) {

               }

               override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
                   var size: ViewGroup.LayoutParams
                   var bmp1 = resource
                   size = UiHelper.getHeightWidth(this@NewStickerActivity, (w).toInt(), (h).toInt())
                   var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
                   var drawable = BitmapDrawable(resources, resizedBitmap)
                   var dsImageSticker = DrawableSticker(drawable)
                   dsImageSticker.setTag("ImageSticker")
                   var pm: List<Int>
                   if (density > 3.0) {
                       pm = UiHelper.getmargins(this@NewStickerActivity, (x).toInt(), (y).toInt())
                   } else {
                       pm = UiHelper.getmargins(this@NewStickerActivity, (x).toInt(), (y).toInt())
                   }
                   Log.i("Hmmm:", pm.get(0).toFloat().toString() + "::" + pm.get(1).toFloat().toString())

                   stickerView.addStickerAndSetMatrix1(
                           dsImageSticker,
                           pm.get(0).toFloat(),
                           pm.get(1).toFloat()
                   )

               }

           })
    }
   fun setTextSticker(text: String, color: Int,size: Int, x: Int, y: Int){
            val bmp1: Bitmap
            val drawable: Drawable
            var l: List<Int>
            if (density > 3.0) {
                l = UiHelper.getmargins(this@NewStickerActivity, (x).toInt(), (y * 1.07).toInt())
            } else {
                l = UiHelper.getmargins(this@NewStickerActivity, x.toInt(), y.toInt())
            }
            //var tf = Typeface.createFromFile(assets,"fonts/"+path)
            var tf = Typeface.createFromAsset(assets, "fonts/Myriad Pro Bold SemiExtended.ttf")
            bmp1 = createBitmapFromLayoutWithText(this@NewStickerActivity, size, text, color, 0f, tf, 0f, 0f, color, Gravity.LEFT)
            drawable = BitmapDrawable(resources, bmp1)
            var dsTextSticker = DrawableSticker(drawable)
            dsTextSticker.setTag("textSticker")
            Log.i("Hmmm:", l.get(0).toFloat().toString() + "::" + l.get(1).toFloat().toString())
            /*if (rotate) {
                stic.addStickerAndSetrotate(
                        dsTextSticker, rotationdegress,
                        l.get(0).toFloat(),
                        l.get(1).toFloat()
                )
            } else {*/
            stickerView.addStickerAndSetMatrix1(
                    dsTextSticker,
                    l.get(0).toFloat(),
                    l.get(1).toFloat())



    }

更新

我在没有协程的情况下通过按顺序递增和获取图像来完成这项工作: 首先我取了一个 Int 然后不断增加直到它达到列表大小这是我的代码: 首先我这样做了:

var i = 0 
setimagestickers(imagestickers.get(i).path!!, imagestickers.get(i).x!!, imagestickers.get(i).y!!, imagestickers.get(i).width!!, imagestickers.get(i).height!!)

在那之后,资源准备好了!

Glide.with(this@NewStickerActivity).asBitmap().timeout(6000000).load(path).into(object : CustomTarget<Bitmap>() {
               override fun onLoadCleared(placeholder: Drawable?) {

               }

               override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
                   var size: ViewGroup.LayoutParams
                   var bmp1 = resource
                   size = UiHelper.getHeightWidth(this@NewStickerActivity, (w).toInt(), (h).toInt())
                   var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
                   var drawable = BitmapDrawable(resources, resizedBitmap)
                   var dsImageSticker = DrawableSticker(drawable)
                   dsImageSticker.setTag("ImageSticker")
                   var pm: List<Int>
                   if (density > 3.0) {
                       pm = UiHelper.getmargins(this@NewStickerActivity, (x).toInt(), (y).toInt())
                   } else {
                       pm = UiHelper.getmargins(this@NewStickerActivity, (x).toInt(), (y).toInt())
                   }
                   Log.i("Hmmm:", pm.get(0).toFloat().toString() + "::" + pm.get(1).toFloat().toString())

                   stickerView.addStickerAndSetMatrix1(
                           dsImageSticker,
                           pm.get(0).toFloat(),
                           pm.get(1).toFloat()
                   )
                i++
                if(i < imagestickers.size){
                    setimagestickers(imagestickers.get(i).path!!, imagestickers.get(i).x!!, imagestickers.get(i).y!!, imagestickers.get(i).width!!, imagestickers.get(i).height!!)
                }
                   else{
                    if(textstickers.isNullOrEmpty()){
                        loader!!.hide()
                    }
                    else {
                        setTextSticker(textstickers.get(j).text!!, Color.parseColor(textstickers.get(j).color), textstickers.get(j).size!!, textstickers.get(j).x!!, textstickers.get(j).y!!)
                    }
                    }
               }

           })

但是我仍然想知道如何使用协程而不是这种方法来解决它!!!

【问题讨论】:

  • 为什么你需要在runBlocking内部调用launch
  • 我不需要它,我知道但运行阻塞对我不起作用,所以我只是尝试加入,但似乎没有帮助! @Ircover
  • 如果只是按顺序同步加载图片,行吗? (因为在示例中显然您希望避免任何并行性并仅按顺序加载图像。)可能是 HTTP 请求在完全加载过大的图像之前就超时了?
  • @9000 检查我更新的问题

标签: kotlin kotlin-coroutines


【解决方案1】:

基本上,您在进行异步 Glide 调用时不会暂停协程,直到它们完成。您必须更改的第一件事是引入suspend fun getImageSticker()

suspend fun getSticker(path: String): Bitmap =
    suspendCancellableCoroutine { continuation -> Glide
        .with(this@NewStickerActivity)
        .asBitmap()
        .timeout(6000000)
        .load(path)
        .into(object : CustomTarget<Bitmap>() {
            override fun onResourceReady(resource: Bitmap, x: Transition<in Bitmap>?) {
                continuation.resume(resource)
            }
        })
    }

请注意,这将返回 Bitmap。现在您可以像调用常规的阻塞函数一样调用它,并从调用方使用它的结果:

suspend fun setimagestickers(path: String, x: Int, y: Int, w: Int, h: Int) {
    val resource = getSticker(path)
    var size: ViewGroup.LayoutParams
    var bmp1 = resource
    size = UiHelper.getHeightWidth(this@NewStickerActivity, (w).toInt(), (h).toInt())
    var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)

}

现在,如果您想并行化 setimagestickers 调用,请在循环中启动它们:

launch {
    imagestickers.map {
        launch {
            var image = it.path
            var x = it.x
            var y = it.y
            image!!.log()
            setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
        }
    }.joinAll()
    textstickers.forEach {
        ...
    }
}

【讨论】:

  • 没有按预期工作,按照你说的实施后我的活动冻结并停止响应
  • 是的,这就是具有多层问题的问题所发生的情况。我的答案解决了其中两个。您在其上下文之外显示了代码,但是如果您在 UI 线程上运行的函数中编写了 runBlocking,它当然会阻止它。尝试替换为launch,但您还需要正确的CoroutineScope
  • 用主协程范围替换运行阻塞后完美运行!!
【解决方案2】:

您的任何代码都不会从 runBlocking 或您启动的协程中受益。

如果由于每个任务的长期运行性质,您的目标是简单地并行处理每个 Image 类型对象,那么您可以执行以下操作:

fun main() {
    runBlocking {
        imagestickers.map {
            launch {
                var image = it.path
                var x = it.x
                var y = it.y
                image!!.log()
                setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
            }
        }.joinAll()
        textstickers.map {
            launch {
                var text = it.text.toString()
                var color = it.color
                var font = it.font
                var size = it.size
                var x = it.x
                var y = it.y
                setTextSticker(text, Color.parseColor(color), size!!, x!!, y!!)
            }
        }.joinAll()
    }
}

为什么我删除了你正在做的一些东西:

  • 嵌套的runBlocking 调用什么也没做。 `runBlocking 的意思是“阻塞直到该块内的任何内容完成”。您的 for 循环不是异步的,并且会一直运行到它们完成为止。
  • 第一个协程launch 你也没有做任何事情。您启动了它,然后立即致电join。这意味着您启动了一个协程,然后立即等到它完成后再继续。

这是做什么的:

  • 我们不是循环,而是将每个图像对象映射到一个作业列表,以便同时运行协同程序。
  • 然后,我们等待每个列表中的每个作业都完成,然后再继续下一个列表。

请注意:看起来您的代码不会从非阻塞 IO 中受益,在这种情况下,这种并行性将受限于您拥有的线程数量。除非您使用在获取图像时正确生成的非阻塞库,否则获取图像将阻塞它所在的线程(无论使用协程)。

【讨论】:

  • 不,这在应用您建议的更改后不起作用 textstickers 列表在 imagestickers 列表完成之前仍在执行,我知道为什么在调用 imagestickers 列表时会发生这种情况时间,因为它是从服务器加载的,因此我们需要以某种方式将 gilde 代码添加到阻塞中,但是如何呢??
  • runBlocking 并不神奇。如果 Glide 正在异步运行某些东西,并且在您调用它时并没有真正完成 fetch,那么您需要找到一种方法来等待它完成。这是一个Glide 图书馆问题...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-22
  • 2019-01-11
  • 2021-09-18
  • 2020-07-10
相关资源
最近更新 更多