让我们看一下我们有一个视图的情况,例如ImageView,我们必须在渲染之前首先准备它-例如,计算它的大小,形状或应用蓝光效果等。这些计算可能是一项昂贵的操作,因此最好将它们传输到后台线程。Javista的祖父将创建一个符文,然后使用处理程序将结果传输到主流并在视图上使用它(首先想到的)。如何在带有协程的大锅中快速方便地完成此操作:首先,创建kotlin-extension函数:inline fun <T> View.doAsync(
crossinline backgroundTask: (scope: CoroutineScope) -> T,
crossinline result: (T?) -> Unit) {
val job = CoroutineScope(Dispatchers.Main)
val attachListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(p0: View?) {}
override fun onViewDetachedFromWindow(p0: View?) {
job.cancel()
removeOnAttachStateChangeListener(this)
}
}
this.addOnAttachStateChangeListener(attachListener)
job.launch {
val data = async(Dispatchers.Default) {
try {
backgroundTask(this)
} catch (e: Exception) {
e.printStackTrace()
return@async null
}
}
if (isActive) {
try {
result.invoke(data.await())
} catch (e: Exception) {
e.printStackTrace()
}
}
this@doAsync.removeOnAttachStateChangeListener(attachListener)
}
}
现在我们将模拟一个情况:我们有一个RecyclerView,在每个项目中都有一张图片。放映前,我们要使这张照片模糊(模糊)。没有异步的情况如下:inner class PostHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val ivTest = itemView.iv_test
fun bind() {
val bitmap = ...
val blurBitmap = bitmap?.addBlurShadow(Color.CYAN, 50.dp, 50.dp)
ivTest.setImageBitmap(blurBitmap)
}
}
结果:
如您所见,人员流失是巨大的。现在我们使用我们的函数:inner class PostHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val ivTest = itemView.iv_test
fun bind() {
val bitmap = ...
itemView.doAsync({ scope ->
return@doAsync bitmap?.addBlurShadow(Color.CYAN, 50.dp, 50.dp)
}, { it ->
ivTest.setImageBitmap(it)
})
}
}
结果:
整个问题是我们依附于视图的生命周期。因此,当视图与父视图分离时,当物品丢失时在回收站中不断发生这种情况,或者销毁它们时在碎片/活动中不断发生这种情况,我们可以安全地停止并取消协程,知道结果无处可显示,视图就可以销毁了。为了使内容清晰易懂,我建议在ViewHolder中编写这样的代码并查看日志:itemView.doAsync({ scope ->
logInfo("coroutine start")
var x = 0
while (x < 100 && scope.isActive) {
TimeUnit.MILLISECONDS.sleep(100)
logInfo("coroutine, position: $adapterPosition ${x++}")
}
logInfo("coroutine end")
}, {
logInfo("coroutine DONE")
})
您将看到哪个协程程序在哪个Viewholder上开始工作,在哪个Viewholder上取消并停止工作。