Kotlin Блокировка запуска Coroutines не работает должным образом и не может заблокировать выполнение циклов for - PullRequest
1 голос
/ 21 января 2020

В моем приложении я выполняю два цикла for, однако циклы for должны быть запланированы в порядке, описанном здесь:

Существует два цикла for: 1- ImageStickerslist 2-TextStickerslist

То, что я хочу сделать, это после imagestickerslist, если он будет корректно составлен только тогда, будет выполнен текстовый список.

Здесь список imagesticker состоит из 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!!)
                    }
                    }
               }

           })

Однако я все еще интересно, как я могу решить это с сопрограммами, а не такой подход !!!

Ответы [ 2 ]

1 голос
/ 22 января 2020

По сути, вы делаете асин c Слайд-вызовы без приостановки сопрограммы, пока они не будут выполнены. Первое, что вы должны изменить, это ввести 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 вызовы, запустите их в любой oop:

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 {
        ...
    }
}
0 голосов
/ 21 января 2020

Ни один из вашего кода не получит выгоду от runBlocking или запускаемых вами сопрограмм.

Если вы хотите просто обработать каждый из ваших объектов типа Image в paralellel из-за длительного выполнения каждой задачи, то вы может сделать что-то вроде этого:

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 не были асинхронными c и будут работать до тех пор, пока они не будут выполнены.
  • Первая сопрограмма launch, которую вы тоже ничего не сделали. Вы запустили его, а затем сразу же позвонили join. Это означает, что вы запускаете сопрограмму, но затем сразу ждете, пока она не будет завершена, прежде чем продолжить.

Что это делает:

  • Вместо того, чтобы зацикливать, мы отображаем каждый объект изображения к списку заданий для одновременно запущенных сопрограмм.
  • Затем мы ждем, пока каждое задание завершится sh в каждом списке, прежде чем перейти к следующему списку.

Обратите внимание: это не похоже, что какой-либо из ваших кодов выигрывает от неблокирующего ввода-вывода, и в этом случае этот паррализм будет ограничен количеством имеющихся у вас потоков. Эта выборка изображения заблокирует поток, в котором оно находится (независимо от использования сопрограмм), если вы не используете неблокирующую библиотеку, которая правильно выдает, пока она выбирает ваше изображение.

...