Android Firestore数据过滤失效并抛空指针异常,请求排查
Android Firestore结合RecyclerView Filter过滤时抛出NullPointerException
问题描述
开发Android应用时,尝试通过Firestore结合RecyclerView的Filter实现数据过滤功能,功能无法正常工作,抛出NullPointerException,异常指向publishResults方法中的datalist.addAll((ArrayList<RecyclerviewModel>)filterResults.values)行,无其他额外错误提示。
错误日志
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object[] java.util.Collection.toArray()' on a null object reference at java.util.ArrayList.addAll(ArrayList.java:588) at com.example.sharedviewmodelapp.RecyclerviewAdapter$2.publishResults(RecyclerviewAdapter.java:239)
相关代码
RecyclerView适配器构造方法
ArrayList<RecyclerviewModel> datalist; ArrayList<RecyclerviewModel> backup; Context context; public RecyclerviewAdapter(ArrayList<RecyclerviewModel> datalist, Context context, HomeFragment homeFragment) { this.datalist = datalist; this.context = context; this.homeFragment = homeFragment; backup = new ArrayList<>(datalist); }
RecyclerView Filter实现代码
@Override public Filter getFilter() { Filter filter = new Filter() { @Override protected FilterResults performFiltering(CharSequence keyword) { ArrayList<RecyclerviewModel> filteredData = new ArrayList<>(); if (keyword.toString().isEmpty()) { filteredData.addAll(backup); }else { firebaseFirestore.collection("users").whereEqualTo("search", keyword.toString().toLowerCase()).get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() { @Override public void onSuccess(QuerySnapshot queryDocumentSnapshots) { datalist.clear(); if (!queryDocumentSnapshots.isEmpty()) { for (RecyclerviewModel obj : backup) { if (obj.getName().toString().toLowerCase().contains(keyword.toString().toLowerCase())) { filteredData.add(obj); } } }else { Toast.makeText(context, "data not exist", Toast.LENGTH_SHORT); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); } }); } FilterResults results = new FilterResults(); results.values = filteredData; results.count = filteredData.size(); return results; } @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { datalist.clear(); datalist.addAll((ArrayList<RecyclerviewModel>)filterResults.values); notifyDataSetChanged(); } }; return filter; }
MainActivity中SearchView代码
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { recyclerviewAdapter.getFilter().filter(newText); return false; } });
问题分析与解决方案
核心错误点
- Filter中执行异步操作违反设计逻辑:
performFiltering方法运行在后台线程,Firestore的get()是异步回调操作,回调会在主线程执行。这导致performFiltering返回FilterResults时,Firestore查询还未完成,filteredData的填充逻辑滞后,甚至可能因线程竞争导致filterResults.values出现空值。 - Firestore查询结果未被实际使用:代码中发起了Firestore查询,但后续遍历本地
backup列表做过滤,完全浪费了云端查询的结果,逻辑无效。 - 数据源被重复清空:在
onSuccess中调用datalist.clear()后,publishResults又再次调用datalist.clear(),可能引发数据状态混乱。 - Toast未触发显示:
Toast.makeText(...)未调用show()方法,不会弹出提示信息。
针对性修复方案
方案一:本地同步过滤(数据已全量加载到本地)
如果backup列表已包含所有需要过滤的数据,直接使用本地过滤符合RecyclerView Filter的同步设计:
@Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence keyword) { ArrayList<RecyclerviewModel> filteredData = new ArrayList<>(); String lowerCaseKeyword = keyword.toString().toLowerCase().trim(); if (lowerCaseKeyword.isEmpty()) { filteredData.addAll(backup); } else { for (RecyclerviewModel obj : backup) { if (obj.getName().toString().toLowerCase().contains(lowerCaseKeyword)) { filteredData.add(obj); } } } FilterResults results = new FilterResults(); results.values = filteredData; results.count = filteredData.size(); return results; } @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { datalist.clear(); // 增加空值判断,避免空指针 if (filterResults.values != null) { datalist.addAll((ArrayList<RecyclerviewModel>) filterResults.values); } notifyDataSetChanged(); } }; }
方案二:Firestore异步查询过滤(放弃Filter类)
如果数据量过大无法全量加载到本地,直接在SearchView监听中发起Firestore查询,更新数据源:
修改MainActivity的SearchView代码:
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { String lowerCaseQuery = newText.toLowerCase().trim(); if (lowerCaseQuery.isEmpty()) { // 恢复显示全部数据 datalist.clear(); datalist.addAll(backup); recyclerviewAdapter.notifyDataSetChanged(); } else { // 发起Firestore查询并更新数据源 firebaseFirestore.collection("users") .whereEqualTo("search", lowerCaseQuery) .get() .addOnSuccessListener(queryDocumentSnapshots -> { datalist.clear(); if (!queryDocumentSnapshots.isEmpty()) { for (QueryDocumentSnapshot doc : queryDocumentSnapshots) { // 将Firestore文档转换为模型对象 RecyclerviewModel model = doc.toObject(RecyclerviewModel.class); datalist.add(model); } } else { Toast.makeText(MainActivity.this, "data not exist", Toast.LENGTH_SHORT).show(); } recyclerviewAdapter.notifyDataSetChanged(); }) .addOnFailureListener(e -> Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show() ); } return true; } });
同时移除适配器中的Filter实现,不再依赖Filter类。
额外细节修复
- 确保适配器构造方法中数据源不为空:
public RecyclerviewAdapter(ArrayList<RecyclerviewModel> datalist, Context context, HomeFragment homeFragment) { this.datalist = datalist != null ? datalist : new ArrayList<>(); this.context = context; this.homeFragment = homeFragment; backup = new ArrayList<>(this.datalist); }
- 修复Toast未显示的问题:
Toast.makeText(context, "data not exist", Toast.LENGTH_SHORT).show();
内容的提问来源于stack exchange,提问作者user20008723
相关产品推荐
相关产品推荐

