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

Android中Volley缓存导致RecyclerView出现重复视图问题求助

解决Volley缓存软过期后RecyclerView重复数据的高效方案

你的问题根源在于Volley在缓存软过期时会触发两次onResponse回调:第一次返回本地缓存的数据,第二次返回网络请求获取的最新数据。而你的代码在每次回调时都直接将数据追加到listGrades中,没有清空旧数据,导致最终列表中同时存在缓存和新数据,出现重复。

之前的去重方案在大数据场景下开销高,其实我们可以从根源上避免重复,直接替换数据而非追加,具体方案如下:


1. 修复onResponse的核心处理逻辑

在处理响应数据前,先清空listGrades,再重新添加新数据。这样不管是缓存响应还是网络新响应,都会完全替换掉旧数据,从根本上杜绝重复:

@Override
public void onResponse(JSONArray response) {
    // 关键:先清空列表,避免重复追加数据
    listGrades.clear();
    
    for (int i = 0; i < response.length(); i++) {
        try {
            Categories gradee = new Categories();
            JSONObject json = response.getJSONObject(i);
            gradee.setImageUrl(json.getString(Config.TAG_G_IMAGE_URL));
            gradee.setName(json.getString(Config.TAG_G_NAME));
            listGrades.add(gradee);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    
    progressBar.setVisibility(View.GONE);
    adapter.notifyDataSetChanged();
}

2. 移除后台线程中不安全的列表操作

你原来在parseNetworkResponse里的listGrades.clear();是错误的:parseNetworkResponse运行在Volley的后台线程中,直接操作UI线程维护的集合会引发线程安全问题,而且这个时机完全没必要。请删除这行代码,所有列表的修改都放在UI线程的onResponse中处理。

3. (可选)优化大数据场景下的RecyclerView更新效率

如果你的数据量很大,notifyDataSetChanged()会刷新整个列表,可能影响性能。可以改用DiffUtil来计算新旧数据的差异,只更新变化的项:

// 自定义DiffUtil回调类,用于判断数据差异
public class CategoriesDiffCallback extends DiffUtil.Callback {
    private List<Categories> oldList;
    private List<Categories> newList;

    public CategoriesDiffCallback(List<Categories> oldList, List<Categories> newList) {
        this.oldList = oldList;
        this.newList = newList;
    }

    @Override
    public int getOldListSize() {
        return oldList.size();
    }

    @Override
    public int getNewListSize() {
        return newList.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        // 根据数据的唯一标识判断是否为同一项(如果Categories有id字段,建议用id判断)
        return oldList.get(oldItemPosition).getName().equals(newList.get(newItemPosition).getName());
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        // 判断项的内容是否完全一致
        Categories oldCat = oldList.get(oldItemPosition);
        Categories newCat = newList.get(newItemPosition);
        return oldCat.getImageUrl().equals(newCat.getImageUrl()) 
                && oldCat.getName().equals(newCat.getName());
    }
}

// 在onResponse中使用DiffUtil更新列表
@Override
public void onResponse(JSONArray response) {
    List<Categories> newList = new ArrayList<>();
    
    for (int i = 0; i < response.length(); i++) {
        try {
            Categories gradee = new Categories();
            JSONObject json = response.getJSONObject(i);
            gradee.setImageUrl(json.getString(Config.TAG_G_IMAGE_URL));
            gradee.setName(json.getString(Config.TAG_G_NAME));
            newList.add(gradee);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    
    // 计算新旧数据差异并更新列表
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new CategoriesDiffCallback(listGrades, newList));
    listGrades.clear();
    listGrades.addAll(newList);
    diffResult.dispatchUpdatesTo(adapter);
    
    progressBar.setVisibility(View.GONE);
}

这个方案的优势在于:

  • 从根源避免重复,不需要额外的去重计算,大数据场景下性能远高于去重方案
  • 所有数据操作都在UI线程完成,避免线程安全问题
  • 可选的DiffUtil优化能进一步提升RecyclerView的更新效率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 20:07:47