You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否不调用notify方法更新RecyclerView?列表项进度条自更新方案咨询

嘿,这个思路完全靠谱!用观察者模式绑定ViewHolder和DownloadManager的进度变化,确实是比定时刷新优雅太多的方案,既能避免不必要的UI线程阻塞,又能减少冗余的RecyclerView刷新操作。我来给你捋捋具体怎么落地:

核心实现方案拆解

本质就是让每个ViewHolder成为对应下载任务进度的订阅者,当DownloadManager里的任务进度更新时,直接通知对应的ViewHolder去更新ProgressBar,全程不用麻烦Adapter调用notifyItemChanged()。

1. 给DownloadManager添加进度发布能力

首先得让DownloadManager能对外发送进度变化的事件。这里用Android自带的LiveData举例(不用额外依赖第三方库,还能自动适配线程),你也可以换成RxJava的Observable或者自己实现简单的观察者模式:

class DownloadManager {
    // 用Map存储每个下载任务ID对应的进度LiveData
    private val progressLiveDataMap = mutableMapOf<String, MutableLiveData<Int>>()

    // 获取指定任务的进度LiveData,不存在则创建
    fun getProgressLiveData(taskId: String): LiveData<Int> {
        return progressLiveDataMap.getOrPut(taskId) { MutableLiveData() }
    }

    // 下载进度更新时,同步更新对应LiveData(postValue确保后台线程调用也能安全更新UI)
    fun updateTaskProgress(taskId: String, progress: Int) {
        progressLiveDataMap[taskId]?.postValue(progress)
    }

    // 任务完成/取消后,移除对应LiveData,避免内存占用
    fun clearTaskProgress(taskId: String) {
        progressLiveDataMap.remove(taskId)
    }
}

2. 让ViewHolder订阅对应任务的进度

在ViewHolder内部,绑定列表项的下载任务时,订阅该任务的进度LiveData,进度变化时直接更新ProgressBar:

class DownloadViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val progressBar: ProgressBar = itemView.findViewById(R.id.download_progress_bar)
    private var currentTaskId: String? = null
    private var progressObserver: Observer<Int>? = null

    fun bind(downloadTask: DownloadTask, downloadManager: DownloadManager) {
        // 先取消之前任务的订阅,避免错误更新和内存泄漏
        currentTaskId?.let { oldTaskId ->
            downloadManager.getProgressLiveData(oldTaskId).removeObserver(progressObserver!!)
        }

        currentTaskId = downloadTask.id
        // 创建新的进度观察者
        progressObserver = Observer { progress ->
            progressBar.progress = progress
            // 进度到100时可以做收尾操作,比如隐藏ProgressBar
            if (progress == 100) {
                progressBar.visibility = View.GONE
            }
        }

        // 订阅进度变化
        downloadManager.getProgressLiveData(downloadTask.id).observeForever(progressObserver!!)
        // 初始化当前进度
        progressBar.progress = downloadTask.currentProgress
    }

    // ViewHolder被回收时,必须取消订阅,防止内存泄漏
    fun onRecycled() {
        currentTaskId?.let { taskId ->
            downloadManager.getProgressLiveData(taskId).removeObserver(progressObserver!!)
        }
        currentTaskId = null
        progressObserver = null
    }
}

3. 让Adapter配合ViewHolder的回收逻辑

Adapter需要重写onViewRecycled方法,调用ViewHolder的回收处理,确保废弃的ViewHolder不会一直持有LiveData引用:

class DownloadAdapter(private val downloadManager: DownloadManager) : RecyclerView.Adapter<DownloadViewHolder>() {
    private val downloadTasks = mutableListOf<DownloadTask>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadViewHolder {
        val itemView = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_download, parent, false)
        return DownloadViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: DownloadViewHolder, position: Int) {
        holder.bind(downloadTasks[position], downloadManager)
    }

    override fun onViewRecycled(holder: DownloadViewHolder) {
        super.onViewRecycled(holder)
        holder.onRecycled()
    }

    // 只有列表结构变化时才调用这个方法更新
    fun refreshTasks(newTasks: List<DownloadTask>) {
        downloadTasks.clear()
        downloadTasks.addAll(newTasks)
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int = downloadTasks.size
}

4. 关键注意事项

  • 内存泄漏防范:ViewHolder被回收时一定要取消订阅,这是重中之重!用observeForever必须手动移除观察者,如果你的场景能绑定Activity/Fragment的生命周期,也可以用observe(lifecycleOwner, observer),但前者的灵活性更高。
  • 线程安全:DownloadManager更新进度时一定要用postValue,因为下载操作肯定在后台线程,postValue会自动把更新切换到主线程,避免UI线程阻塞。
  • 任务清理:下载任务完成或取消后,记得调用clearTaskProgress移除对应的LiveData,避免无用的内存占用。

这样实现后,每个ProgressBar只会在自己对应的任务进度变化时才更新,完全不用定时触发刷新,高效又流畅!

内容的提问来源于stack exchange,提问作者Marty Miller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:50:38