Mine is a chat application and I use MergeAdapter to display 3 sections in the chat Roster listing. Section one includes the list of users that are currently in the thread. Section two lists different groups (consisting of 1+ users) that can be added in the chat and section three is the list of all individual users that can be added in the chat. The list will be something like following:
———Users currently in chat———
UserA
UserB
UserC
UserD
———Groups to add in chat———
Group1
Group2
Group3
Group4
———Users to add in chat———
User1
User2
User3
User4
The user can filter this list by typing characters in an edit text field. I suppose the IllegalStateException occurs when doing the filtering. I cannot reproduce the exception and the only occurrence so far has been on production app.
The exception details are as follows:
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(2131558865, class android.widget.ListView) with Adapter(class com.commonsware.cwac.merge.MergeAdapter)]
at android.widget.ListView.layoutChildren()(ListView.java:1582)
at android.widget.AbsListView$CheckForTap.run()(AbsListView.java:4032)
at android.os.Handler.handleCallback()(Handler.java:739)
at android.os.Handler.dispatchMessage()(Handler.java:95)
at android.os.Looper.loop()(Looper.java:145)
at android.app.ActivityThread.main()(ActivityThread.java:5837)
at java.lang.reflect.Method.invoke()(Method.java:-2)
at java.lang.reflect.Method.invoke()(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run()(ZygoteInit.java:1388)
at com.android.internal.os.ZygoteInit.main()(ZygoteInit.java:1183)
I have just seen this exception once. I am adding the code related to creating and filtering the adapters
public void createAndPopulateMergeAdapter(){
MergeAdapter mergeAdapter = new MergeAdapter();
View convertView = LayoutInflater.from(context).inflate(R.layout.header_row, lvRoster, false);
TextView tvHeader = (TextView) convertView.findViewById(R.id.tvRosterHeader);
tvHeader.setText("Users currently in chat");
mergeAdapter.addView(convertView);
Adapter1 adapter1 = new Adapter1();
mergeAdapter.addAdapter(adapter1);
convertView = LayoutInflater.from(context).inflate(R.layout.header_row, lvRoster, false);
tvHeader = (TextView) convertView.findViewById(R.id.tvRosterHeader);
tvHeader.setText("Groups to add in chat");
mergeAdapter.addView(convertView);
Adapter2 adapter2 = new Adapter2();
mergeAdapter.addAdapter(adapter2);
convertView = LayoutInflater.from(context).inflate(R.layout.header_row, lvRoster, false);
tvHeader = (TextView) convertView.findViewById(R.id.tvRosterHeader);
tvHeader.setText("Users to add in chat");
mergeAdapter.addView(convertView);
Adapter3 adapter3 = new Adapter3();
mergeAdapter.addAdapter(adapter3);
lvRoster.setAdapter(mergeAdapter);
}
public void filter(String searchQuery){
// This will filter the results in adapter1’s underlying list used to display the data
adapter1.filter(searchQuery);
// This will filter the results in adapter2’s underlying list used to display the data
adapter2.filter(searchQuery);
// This will filter the results in adapter3’s underlying list used to display the data
adapter3.filter(searchQuery);
mergeAdapter.notifyDataSetChanged();
}
The filter methods of each adapter will filter the underlying data sources. I call notifyDataSetChanged at the end to reflect the changes on each adapter in the list view. I use Realm as my database and the underlying data source in all individual adapters is a RealmResults<> object so that I can query it directly to apply filters.
The filter method is called from afterTextChanged method of the TextWatcher
@Override
public void afterTextChanged(Editable s) {
filter(s.toString());
}
I am not sure why the crash is occurring and what is causing it. I understand that we need to call notifyDataSetChanged() after every change to the data source of the adapter. My assumption is that MergeAdapter’s notifyDataSetChanged calls notifyDataSetChanged of each added adapter and so I do not need to call notifyDataSetChanged after filtering each adapter. Calling MergeAdapter’s notifyDataSetChanged once at the end should be fine.
I would really appreciate it if someone can help me figure out what am I doing wrong here.
Thanks.