Search code examples
androidandroid-recyclerviewlistadapter

androidx.RecyclerView ListAdapter very slow


I am using the ListAdapter with RecyclerView and under certain circumstances the app becomes extremely slow -- it freezes for 10 seconds with a list of 1000 items.

The circumstances are, that at first I submit a list with 1000 items (at first submit its fast as expected) and then I submit the same list again, but sorted differently.

By debugging a lot, I finally found out, that the ListAdapter triggers a notifyItemRangeChanged(0, 999), so basically for the complete list. I read elsewhere (here and here), that one should not do this, because it makes the RecyclerView slow -- which apparently is true -- however, I cannot influence the behaviour of the ListAdapter.

Does anyone have a solution for that? I don't want to remove ListAdapter again, because for most other usecases it is fast and handy, doing various animations etc. automatically.

EDIT - some code

There is nothing fancy about the code, basically it's like that:

RecyclerView mListView;
EnryListAdapter mEntryListAdapter; // <-- extends ListAdapter<Entry, VH>
...
    mEntryListAdapter = new EntryListAdapter();
    mListView.setAdapter(mEntryListAdapter);
    mListView.setLayoutManager(new LinearLayoutManager(this));
    mListView.setHasFixedSize(true);
    ((DefaultItemAnimator) mListView.getItemAnimator()).setSupportsChangeAnimations(false);

    List<Entry> entryList = getEntryList(); // <-- list with 1000 entries

    mEntryListAdapter.submitList(entryList); // <-- first submit is fast

    entryList = getDifferentlySortedEntryList(); // <-- list with same entries, sorted differently

    mEntryListAdapter.submitList(entryList); // <-- freezes app for over 10 seconds

Solution

  • In the end, I found out that it was my own mistake.

    In my implementation of DiffUtil.ItemCallback<Entry>#areContentsTheSame I had this check:

    oldItem.flags == newItem.flags
    

    where Entry.flags was a long first, but later on I changed it to be an instance of a class, without changing this comparison. Since the instances aren't the same objects, this comparison then resulted in false all the time. Replacing it with

    ObjectsCompat.equals(oldItem.flags, newItem.flags)
    

    fixed the issue.