Veamos una situación en la que tenemos una vista, por ejemplo, ImageView, que primero debemos preparar antes de renderizar, por ejemplo, calcular su tamaño, forma o aplicar un efecto blu-ray, etc. Estos cálculos pueden ser una operación costosa, por lo que es mejor transferirlos al hilo de fondo.Los abuelos de Javista crearán un runabl y luego usarán el controlador para transferir el resultado a la transmisión principal y usarlo en la vista (lo primero que viene a la mente).¿Cómo puede hacerse esto rápida y convenientemente en un caldero con sus corutinas?Primero, cree la función de extensión kotlin: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)
}
}
Ahora simularemos la situación: tenemos un RecyclerView, en cada elemento hay una imagen. Antes del espectáculo, queremos desenfocar esta imagen (desenfoque). Así es como sería sin asincronización: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)
}
}
Resultado:
como puede ver, la pérdida de personal es significativa.Ahora usamos nuestra función: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)
})
}
}
Resultado:
todo el asunto es que estamos apegados al ciclo de vida de la vista. Por lo tanto, cuando la vista se separa del padre, y esto sucede constantemente en el reciclador cuando se pierde el artículo, o en el fragmento / actividad cuando se destruyen, podemos detener y cancelar la rutina de manera segura, sabiendo que el resultado no se puede mostrar, la vista está lista para la destrucción.Para que sea claro y comprensible, recomiendo escribir dicho código en su ViewHolder y mirar los registros: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")
})
Y verá en qué Viewholder qué corutina comienza a funcionar y en qué se cancela y deja de funcionar.