如何在回收器适配器项目中保存一些数据以在视图被回收时取消飞行中的请求?

How to save some data in a recycler adapter item to cancel requests in flight when a view is recyclerd?

我有一个回收器适配器,它在滚动时从网络加载内容(包括缩略图和其他一些数据)。

但是,当我滚动很长的距离时,适配器会尝试对请求进行排队,以便加载起点和点之间的所有项目,然后在新位置的项目之前显示。我不想像这样快速滚动过去时继续加载这些中间项。

我尝试编写以下代码来执行此操作:

class SomeRecyclerAdapter(private val dataset: MutableList<SomeData>)
    : RecyclerView.Adapter<SongsRecylerAdapter.ViewHolder>() {

    // ... irrelevant fields here

    var requestInFlight: Job? = null
    var requestInFlight2: Job? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val someListItem = LayoutInflater.from(parent.context)
                .inflate(R.layout.list_item_foo, parent, false)

        return ViewHolder(someListItem)
    }

    override fun getItemCount() = dataset.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val someData = dataset[position]
        holder.someView.text = someData.toString()
        holder.thumbnailView.imageResource = R.drawable.default_thumbnail

        // Cancel any requests currently in flight, to prevent janky stuff on long scrolls
        // Otherwise, if scrolling far, it will load all of the data in sequence
        // We don't want to load the stuff in between before loading the stuff where we are at
        requestInFlight?.cancel()
        requestInFlight2?.cancel()

        requestInFlight = async(UI) {
            var someDataFromServer = listOf<Bar>()

            async(CommonPool) {
                someDataFromServer = someSuspendMethod()
            }.await()

            someData.thumbnailUri = someDataFromServer.thumbnailUri

            GlideApp.with(holder.thumbnailView.context)
                    .load(dataset[position].thumbnailUri)
                    .placeholder(R.drawable.default_thumbnail)
                    .into(holder.thumbnailView)
        }
    }

    // Viewholder here...

}

所以,我想我知道为什么它不起作用。当我在飞行中取消作业时,我实际上取消了上一个启动的调用,而不是正在重新加载的回收器视图元素的调用。但是,我不确定如何将作业与此元素相关联,这样当此视图被回收时,它会取消它自己的正在进行的请求。

有什么帮助吗?

requestInFlightrequestInFlight2 放入 ViewHolder 字段,因为每个字段都有自己的加载,因为现在您在滚动期间错误地覆盖了作业。

然后将您的取消逻辑移动到 onViewRecycled:

override fun onViewRecycled(holder: ViewHolder){
    holder.requestInFlight?.cancel()
    holder.requestInFlight2?.cancel()
}

请注意,协同程序持有对它们在异步块中引用的任何对象的强引用,因此您可能会在屏幕旋转时泄漏上下文。