2

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.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
rbing
  • 133
  • 1
  • 10
  • I am fairly stunned that your use of `MergeAdapter` with filtering has survived as well as it has. That certainly isn't something I have tried. "My assumption is that MergeAdapter’s notifyDataSetChanged calls notifyDataSetChanged of each added adapter" -- no. It is the other way around: if you call `notifyDataSetChanged()` on one of the merged adapters, `MergeAdapter` catches that and forwards it along. – CommonsWare Feb 28 '17 at 21:55
  • @CommonsWare Thanks for your prompt reply. I have been using the filtering a lot and since long. I haven't seen any issues so far except this one exception. Would you recommend not to use filtering with MergeAdapter? Also do you think calling notifiyDataSetChanged after filtering each adapter will prevent the IllegalStateException? Thanks again. – rbing Feb 28 '17 at 22:05
  • "Would you recommend not to use filtering with MergeAdapter?" -- it is more that I have no idea what to expect. "Also do you think calling notifiyDataSetChanged after filtering each adapter will prevent the IllegalStateException?" -- I do not know. If it is a problem with the filtering, I would expect to occur all the time, and if I am understanding your situation correctly, you cannot reproduce the problem. Usually, when I see unreproducible problems like this, I tend to focus on configuration changes and threads. – CommonsWare Feb 28 '17 at 22:07
  • @CommonsWare Thank you again for the reply. I think I will try and see if there are any threading issues and if I can find the reason behind the crash. Meanwhile I will just call notifyDataSetChanged only once at the end and won't change my code. – rbing Feb 28 '17 at 22:12

0 Answers0