Kotlin协程运行阻塞时未按预期工作,无法阻塞for循环的执行
在我的应用程序中,我正在执行两个for循环,但是那些for循环需要按顺序安排,以下是用例: 有两个for循环: 1-图像标签列表 2-文本标签列表 我想做的是在ImageSticksList之后,如果正确完成,那么只会执行TextSticksList 此处,imagesticker列表包含用于从glide加载图像的url路径,但如果这些图像具有高分辨率,则即使尚未从url加载图像,它最终也会使线程继续。为了解决这个问题,我们尝试在glideonready和full方法中添加阻塞调用,但这并没有任何帮助。我很困惑阻止呼叫是如何工作的。对此,我们将非常感谢您的帮助 下面是执行for循环的代码:Kotlin协程运行阻塞时未按预期工作,无法阻塞for循环的执行,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,在我的应用程序中,我正在执行两个for循环,但是那些for循环需要按顺序安排,以下是用例: 有两个for循环: 1-图像标签列表 2-文本标签列表 我想做的是在ImageSticksList之后,如果正确完成,那么只会执行TextSticksList 此处,imagesticker列表包含用于从glide加载图像的url路径,但如果这些图像具有高分辨率,则即使尚未从url加载图像,它最终也会使线程继续。为了解决这个问题,我们尝试在glideonready和full方法中添加阻塞调用,但这并没有任
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())
}
在那之后,在资源准备就绪的内部做了诀窍
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!!)
}
}
}
})
然而,我仍然想知道如何用协同程序而不是这种方法来解决这个问题 您的任何代码都不会从运行阻塞或启动的协同程序中受益 如果由于每个任务的长时间运行特性,您的目标只是并行处理每个图像类型对象,那么您可以执行以下操作:
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意味着阻塞,直到该块中的内容完成为止。您的for循环不是异步的,并且将一直运行,直到它们完成为止。
你的第一次协同程序发布也没有做什么。您启动了它,然后立即调用join。这意味着您要启动一个协同程序,但在继续之前要立即等待它完成。
它的作用是:
我们不是循环,而是将每个图像对象映射到一个作业列表,以同时运行协同路由。
然后,我们等待每个列表中的每个作业完成,然后再继续下一个列表。
请注意:使用非阻塞IO看起来不会给代码带来任何好处,在这种情况下,这种并行性将限制在您拥有的线程数量上。无论是否使用协程,对映像的获取都将阻止它所在的线程,除非在获取映像时使用一个非阻塞库,该库正确地生成。基本上,在完成之前,您进行异步滑动调用,而不会挂起协程。您必须更改的第一件事是引入suspend fun getImageSticker: 现在,如果要并行化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 {
...
}
}
为什么你需要runBlocking内部的启动调用?我不需要它,我知道,但是runBlocking对我不起作用,所以我只是尝试一下join,但似乎没有帮助@如果你只是按顺序同步加载图像,它能工作吗?因为在本例中,显然您希望避免任何并行性,只需按顺序加载图像。可能是HTTP请求在完全加载过大的图像之前超时了吗?@9000检查我的更新问题否这在应用建议更改后不起作用文本标签列表在imagestickers列表完成之前仍在执行,我知道imagestickers列表被称为图像时为什么会发生这种情况加载glide最终需要时间,因为它是从服务器加载的,因此我们需要以某种方式将gilde代码添加到阻塞中,但运行阻塞并不是魔术。如果Glide异步运行某个东西,并且在调用它时没有实际执行fetch to completion,那么需要找到一种方法等待它完成。这是一个Glide library问题…在按照您所说的方式实施后,我的活动会冻结并停止响应。是的,这就是有多层问题的问题所发生的情况。我的回答解决了其中两个问题。您在其上下文之外显示了代码,但是如果您在UI线程上运行的函数中编写了runBlocking,那么它当然会阻止它。尝试用launch替换,但您还需要适当的协同路由作用域。在用主协同路由作用域替换运行阻塞后,工作非常好!!
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)
}
})
}
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)
}
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 {
...
}
}