文本快速变化时RecyclerView崩溃问题求助
解决RecyclerView快速搜索时的IndexOutOfBoundsException崩溃问题
问题根源分析
你遇到的这个崩溃本质是Filter后台线程与UI线程的数据集同步冲突,加上RecyclerView的状态一致性校验触发的:
performFiltering是在后台线程执行的,但你直接修改了Adapter的成员变量filteredProducts(比如直接替换引用filteredProducts = filteredList或者调用clear()),这会导致UI线程在onBindViewHolder访问数据集时,后台线程可能正在修改它,出现数据不一致。- 竖屏和横屏的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
相关产品推荐
相关产品推荐

