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

文本快速变化时RecyclerView崩溃问题求助

解决RecyclerView快速搜索时的IndexOutOfBoundsException崩溃问题

问题根源分析

你遇到的这个崩溃本质是Filter后台线程与UI线程的数据集同步冲突,加上RecyclerView的状态一致性校验触发的:

  1. performFiltering是在后台线程执行的,但你直接修改了Adapter的成员变量filteredProducts(比如直接替换引用filteredProducts = filteredList或者调用clear()),这会导致UI线程在onBindViewHolder访问数据集时,后台线程可能正在修改它,出现数据不一致。
  2. 竖屏和横屏的RecyclerView布局、Item回收复用逻辑不同,快速输入时Filter的高频回调会加剧这种线程冲突,横屏只是刚好没触发这个崩溃点而已,并不是真的没问题。

具体修复方案

1. 修正Filter的数据集操作逻辑

核心原则:后台线程只负责生成过滤结果,绝不直接修改Adapter的数据集;UI线程统一处理数据集更新。修改你的Filter代码如下:

@Override
public Filter getFilter(){
    return new Filter(){
        @Override
        protected FilterResults performFiltering(CharSequence charSequence){
            String searchString = charSequence.toString().toLowerCase();
            List<Product> filteredList = new ArrayList<>();
            
            if(TextUtils.isEmpty(searchString)){
                // 空搜索时返回空列表(或者你需要的默认数据集)
                filteredList.clear();
            } else{
                // 优化:提前转换为小写,避免循环里重复调用toLowerCase()
                for(Product product : ProductUtil.allProducts){
                    String name = product.getName().toLowerCase();
                    String category = product.getCategory().toLowerCase();
                    String sku = product.getSku().toLowerCase();
                    String skuNoDash = sku.replace("-", "");
                    
                    if(name.contains(searchString) 
                       || category.contains(searchString)
                       || sku.contains(searchString)
                       || skuNoDash.contains(searchString)){
                        filteredList.add(product);
                    }
                }
            }
            
            FilterResults filterResults = new FilterResults();
            filterResults.values = filteredList;
            filterResults.count = filteredList.size(); // 手动设置count,让Filter逻辑更可靠
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence sequence, FilterResults filterResults){
            // publishResults在UI线程执行,安全更新数据集
            filteredProducts.clear();
            filteredProducts.addAll((List<Product>) filterResults.values);
            notifyDataSetChanged();
        }
    };
}

这里的关键是:后台线程只生成新列表,不碰Adapter的成员变量;UI线程通过clear()+addAll()的方式更新,避免直接替换引用导致的线程安全问题。

2. 增加线程安全保护(可选但推荐)

给数据集的访问和修改加同步锁,避免多线程同时操作:

// 在ProductAdapter里添加一个锁对象
private final Object dataLock = new Object();

// 修改onBindViewHolder,加锁访问数据集
@Override
public void onBindViewHolder(@NonNull ProductViewHolder holder, final int position) {
    Product mCurrent;
    synchronized (dataLock) {
        mCurrent = filteredProducts.get(position);
    }
    // 后续绑定逻辑不变
    holder.productName.setText(mCurrent.getName());
    // ...
}

// 修改publishResults,加锁更新数据集
@Override
protected void publishResults(CharSequence sequence, FilterResults filterResults){
    synchronized (dataLock) {
        filteredProducts.clear();
        filteredProducts.addAll((List<Product>) filterResults.values);
    }
    notifyDataSetChanged();
}

3. 降低Filter触发频率(缓解快速输入压力)

在搜索框的TextWatcher里增加延迟触发,避免每次输入字符都立刻启动Filter任务:

// 在你的Activity/Fragment中
private Handler searchHandler = new Handler();
private Runnable searchRunnable;

// 给搜索框添加监听
searchEditText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // 移除之前未执行的搜索任务
        if(searchRunnable != null){
            searchHandler.removeCallbacks(searchRunnable);
        }
        // 延迟300ms再执行过滤,避免快速输入时频繁触发
        searchRunnable = () -> productAdapter.getFilter().filter(s);
        searchHandler.postDelayed(searchRunnable, 300);
    }

    @Override
    public void afterTextChanged(Editable s) {}
});

4. 优化RecyclerView配置(可选)

如果你的RecyclerView高度固定,可以开启setHasFixedSize(true),减少不必要的布局计算,降低状态不一致的概率:

recyclerView.setHasFixedSize(true);
recyclerView.setItemViewCacheSize(20); // 增加Item缓存,减少复用冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:51:47