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

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;
        }
    });

问题分析与解决方案

核心错误点

  1. Filter中执行异步操作违反设计逻辑:performFiltering方法运行在后台线程,Firestore的get()是异步回调操作,回调会在主线程执行。这导致performFiltering返回FilterResults时,Firestore查询还未完成,filteredData的填充逻辑滞后,甚至可能因线程竞争导致filterResults.values出现空值。
  2. Firestore查询结果未被实际使用:代码中发起了Firestore查询,但后续遍历本地backup列表做过滤,完全浪费了云端查询的结果,逻辑无效。
  3. 数据源被重复清空:在onSuccess中调用datalist.clear()后,publishResults又再次调用datalist.clear(),可能引发数据状态混乱。
  4. 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类。

额外细节修复

  1. 确保适配器构造方法中数据源不为空:
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);
}
  1. 修复Toast未显示的问题:
Toast.makeText(context, "data not exist", Toast.LENGTH_SHORT).show();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:11:13