Why I am getting the List is null on my phone and not on the virtual devices?

I implemented a search functionality in custom list view to my application. My problem is that I am getting this error (just on the real phones, not the virtuals):

java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference 

I don’t understand why on virtual devices the search functionality works like a charm. Where does the problem lie? This is my code:

CustomAdapterShoppingList class:

public class CustomAdapterShoppingList extends BaseAdapter implements Filterable {

    List<Product> list;
    List<Product> listFiltered;
    Context mContext;

    public CustomAdapterShoppingList(@NonNull Context context, List<Product> products) {
        this.list = products;
        this.listFiltered = products;
        this.mContext = context;
    }

    @Override
    public int getCount() {
        return listFiltered.size();  <---- THE ERROR mentioned above
    }

    @Override
    public Product getItem(int position) {
        return listFiltered.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    private int lastPosition = -1;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.shoplist_row, parent,
                    false);
        }
        Animation animation = AnimationUtils.loadAnimation(mContext, (position > lastPosition)
                ? R.anim.up_from_bottom : R.anim.down_from_top);
        convertView.startAnimation(animation);
        lastPosition = position;

            TextView number = convertView.findViewById(R.id.number);
            number.setText(position + 1 + ".");

            TextView name = convertView.findViewById(R.id.name);
            name.setText(listFiltered.get(position).getProduktName());
            
        return convertView;
    }

    @Override
    public Filter getFilter() {
        Filter filter = new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                FilterResults filterResults = new FilterResults();
                if (list == null) {
                    list = new ArrayList<>(listFiltered);
                }

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
                    if (charSequence == null || charSequence.isEmpty()) {
                        filterResults.count = list.size();
                        filterResults.values = list;
                    } else {
                        List<Product> resultPrd = new ArrayList<>();
                        String searchStr = charSequence.toString().toLowerCase();

                        for (Product prd:list) {
                            if(prd.getProduktName().contains(searchStr)) {
                                resultPrd.add(prd);
                            }
                            filterResults.count = resultPrd.size();
                            filterResults.values = resultPrd;
                        }
                    }
                }
                return filterResults;
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {

                listFiltered = (List<Product>) filterResults.values;
                notifyDataSetChanged();
            }
        };
        return filter;
    }
}

Then, calling The search functionality in ShoppinglistActivity class with this code:

adapter = new CustomAdapterShoppingList(getApplicationContext(), products);
                shoppingList.setAdapter(adapter);
                adapter.notifyDataSetChanged();
                sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                    @Override
                    public boolean onQueryTextSubmit(String query) {
                        return false;
                    }
                    @Override
                    public boolean onQueryTextChange(String newText) {
                        adapter.getFilter().filter(newText);
                        return false;
                    }
                });

Your NullPointerException comes from this line:

return listFiltered.size();

This means listFiltered is null when getCount() is called — which typically happens when the ListView is trying to render itself.

Why It Works on Emulator but Crashes on Real Device

Real devices often behave slightly differently due to:

  • Memory management differences
  • API level or Android version
  • Different lifecycle timing (like rendering faster and calling getCount() before the list is filtered)

Problem Root Cause

In your performFiltering() method:

if (list == null) {
    list = new ArrayList<>(listFiltered); // ← If listFiltered is null, this line throws
}

But more importantly, when the query is empty, you never set filterResults.values if the condition fails for Android versions lower than VANILLA_ICE_CREAM (which doesn’t even exist — you probably meant ICE_CREAM_SANDWICH, which is Build.VERSION_CODES.ICE_CREAM_SANDWICH).

Also, if filtering returns nothing, filterResults.values can be null, and this will make listFiltered = null → crash on getCount().


Fix: Ensure listFiltered is Never Null

Update your publishResults() like this:

@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
    if (filterResults != null && filterResults.values != null) {
        listFiltered = (List<Product>) filterResults.values;
    } else {
        listFiltered = new ArrayList<>(); // fallback to empty list
    }
    notifyDataSetChanged();
}

Also Fix Android Version Constant

Replace:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM)

with:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)

or better, remove the version check entirely if it’s not necessary — your filtering logic doesn’t depend on API level.


Defensive Programming

In your constructor, make sure both lists are initialized:

this.list = products != null ? products : new ArrayList<>();
this.listFiltered = new ArrayList<>(this.list);

Summary

  • Always ensure listFiltered is not null before accessing it.
  • Default to an empty list in edge cases.
  • Fix invalid SDK version reference (VANILLA_ICE_CREAM is not a real constant).