首页 > 解决方案 > 视图中的协程作业在哪里取消?

问题描述

我在视图(自定义视图)中使用协程,这些视图被添加到回收器视图中。

我在协程上运行的大部分计算(复杂且长)scopeIO并更新scopeMain.

但是,当这些视图被添加到 Recycler View 时,它可以完美地工作,但是当我快速滚动时,即使视图不再可见,作业也会在后台运行。

我不确定在哪里取消作业。我见过的大多数示例代码都是正确定义生命周期的活动中的协程。

我尝试取消作业,onDetachedFromWindow但是当我在回收站视图中快速滚动时,它取消了作业,而当视图再次附加到窗口时,它是空视图。

下面是我的协程声明

 CustomView{
    .... 
    ....
    private val job = Job()
    private val scopeMain = CoroutineScope(job + Dispatchers.Main)
    private val scopeIO = CoroutineScope(job + Dispatchers.IO)
    .... 
    ....
    }

由于上述原因,许多工作仍然存在,整个应用程序变得滞后,并且在某些时候也会崩溃。

PS-是否有可能有一个可用于渲染整个 recyclerView 的作业,即所有孩子(CustomView)的相同作业,我相信新的 Job() 创建会导致滞后。

我正在进行的演示项目:

已经提到这个

标签: androidkotlinkotlin-coroutines

解决方案


您可以覆盖' onViewDetachedFromWindow() '并在相应的作业上调用cancel()。

onViewDetachedFromWindow 在视图变得不可见并且适合您的目的时被调用。

从安卓文档:

当 RecyclerView.LayoutManager 决定不再需要将视图附加到其父 RecyclerView 时,视图将被回收。这可能是因为它已失去可见性,或者由仍附加到父 RecyclerView 的视图表示的一组缓存视图。如果项目视图绑定了大型或昂贵的数据,例如大型位图,那么这可能是释放这些资源的好地方。

链接在这里

override fun onViewDetachedFromWindow(holder: ViewHolder) {
    holder.yourView.cancelJob()
}

同样,当您的视图再次出现时:

onViewAttachedToWindow(holder:ViewHolder)

将被调用。在这里,您必须重新启动协程作业。

holder.yourJob.start()

您的问题的原因是,您正在取消作业,但是当再次查看时(它没有再次创建,它正在被重新使用,即绑定已完成到已创建的视图),因此您需要开始自己的工作。

来自谷歌的文档说:

当此适配器创建的视图已附加到窗口时调用。这可以用作用户即将看到视图的合理信号。如果适配器之前释放了 onViewDetachedFromWindow 中的任何资源,则应在此处恢复这些资源。

链接在这里

现在为您的建议:

整个回收商的 OneJob

您的作业在视图内,因此当创建视图时,作业就会执行。为了让您拥有一份工作,您的工作应该存在于自定义视图之外。您的方法应该接受视图,并且视图应该将视图元素的创建/初始化委托给此方法。这也意味着,您需要将视图内部暴露给这个外部方法。对我来说听起来不是很优雅。此外,如果有 7 个项目可见,您的这个“外部”方法将负责渲染所有 7 个元素,当您快速滚动时,这也会影响性能,因为对于相当大的列表,该方法将很快被淹没。


推荐阅读