使用协程异步使用View

让我们看一下我们有一个视图的情况,例如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,      
    job.launch {
        //  Deferred     
        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()
            }
        }
        //     Job
        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
    //        scope.isActive
    //       isActive = true,     
    //    ,     
    while (x < 100 && scope.isActive) {
        TimeUnit.MILLISECONDS.sleep(100)
        logInfo("coroutine, position: $adapterPosition ${x++}")
    }
    logInfo("coroutine end")
}, {
    logInfo("coroutine DONE")
})

您将看到哪个协程程序在哪个Viewholder上开始工作,在哪个Viewholder上取消并停止工作。

All Articles