androidlistviewandroid-listviewandroid-adapterandroid-filter

application crashed with ANR:: IllegalStateException: The content of the adapter has changed but ListView did not receive a notification.


I created a listview with adapter. Implemented search with filter. It is working fine. But sometimes application is crashing with the following error:

java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes.

In ListView activity I am using the below code to invoke search when user types something in the textbox.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.list_view_layout);
    mAdapter = new ListViewAdapter(this);
    ...

    mSearchEditText = (EditText) findViewById(R.id.search_text);
    mSearchEditText.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            Log.v("ListViewActivity", "SearchTextBox: onTextChanged <" + s + ">");
            mAdapter.getFilter().filter(s);
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });

In ListViewAdapter I used filter to implement search in the listview:

public class ListViewAdapter extends BaseAdapter implements Filterable{
private ArrayList<ListItem> mOriginalList;
private ArrayList<ListItem> mDisplayedList;

...

@Override
public Filter getFilter() {
    Filter filter = new Filter() {

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            Log.v("ListViewAdapter", "Filter: publishResults constraint<" + constraint + ">");
            notifyDataSetChanged();
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            Log.v("ListViewAdapter", "Filter: performFiltering constraint<" + constraint + ">");
            FilterResults filterResults = new FilterResults();

            mDisplayedList.clear();

            if (constraint == null || constraint.length() == 0) {
                mDisplayedList.addAll(mOriginalList);
                filterResults.count = mDisplayedList.size();
                filterResults.values = mDisplayedList;
            } else {
                String searchText = constraint.toString().toLowerCase();
                for (ListItem listItem : mOriginalList) {
                    if (listItem.getKeyName().toLowerCase().contains(searchText) 
                            || listItem.getKeyValue().toLowerCase().contains(searchText)) {
                        mDisplayedList.add(listItem);
                    }
                }

                filterResults.count = mDisplayedList.size();
                filterResults.values = mDisplayedList;
            }

            Log.v("ListViewAdapter", "Filter: performFiltering filterResults.count<" + filterResults.count + ">");
            return filterResults;
        }
    };

    return filter;
}

Solution

  • Issue is resolved. I moved the code to update mDisplayedList to publishResults() instead of keep on updating in performFiltering() for loop. Update mDisplayedList and call notifyDataSetChanged() together. I think this is the best solution.

    @Override
    public Filter getFilter() {
        Filter filter = new Filter() {
    
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                Log.v("ListViewAdapter", "Filter: publishResults constraint<" + constraint + ">");
    
                if (results.count == 0) {
                    mDisplayedList.clear();
                    notifyDataSetInvalidated();
                } else {
                    mDisplayedList.clear();
                    mDisplayedList.addAll((ArrayList<ListItem>) results.values);
                    notifyDataSetChanged();
                }
            }
    
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                Log.v("ListViewAdapter", "Filter: performFiltering constraint<" + constraint + ">");
                FilterResults filterResults = new FilterResults();
                ArrayList<ListItem> filteredList = new ArrayList<ListItem>();
    
                if (constraint == null || constraint.length() == 0) {
                    filteredList.addAll(mOriginalList);
                } else {
                    String searchText = constraint.toString().toLowerCase();
                    for (ListItem listItem : mOriginalList) {
                        if (listItem.getKeyName().toLowerCase().contains(searchText) 
                                || listItem.getKeyValue().toLowerCase().contains(searchText)) {
                            filteredList.add(listItem);
                        }
                    }
                }
    
                filterResults.count = filteredList.size();
                filterResults.values = filteredList;
                Log.v("ListViewAdapter", "Filter: performFiltering filterResults.count<" + filterResults.count + ">");
                return filterResults;
            }
        };
    
        return filter;
    }