RecyclerView and DiffUtil - A Concurrency Nightmare
android, android-recyclerview, android-support-library, concurrency, diff
Solution
I ended up stacking updates to manage user interaction during DiffUtil calculations and still accessing dataset in Main Thread only.
I wrote it there: https://geoffreymetais.github.io/code/diffutil-threading/
Problem
The documentation for `DiffUtil` suggests generating the `DiffUtil.DiffResult` on a background thread because of potential long calculation times. This seems like a bad idea to me, because that thread could be operating on stale data in a situation like the following (assuming `list` access is thread safe): - Add data to `list` and notify adapter - Need to replace `list` with `newList` which would have a diff of some additions and some removals - Call `DiffUtil.calculateDiff` in the background and get the `DiffResult` for `list` and `newList`, and post a message to the main thread that will use `newList` and call `DiffResult.dispatchUpdatesTo` - Before that message is handled, the user takes an action on the main thread that causes mutations to `list` - The message is handled, `newList` is set to be the new data source and `DiffResult.dispatchUpdatesTo` is run causing an inconsistent view of the underlying data + a loss of any mutations since the `DiffResults` were calculated Well that's no good, so let's change starting from step 3: - Set `newList` as the new data source, call `DiffUtil.calculateDiff` in the background and get the `DiffResult` for `list` and `newList`, and post a message to the main thread that will call `DiffResult.dispatchUpdatesTo` - Before that message is handled, the user takes an action on the main thread that causes mutations to `newList`, and notifies adapter, causing an inconsistent view of data because `DiffResult.dispatchUpdatesTo` hasn't been called yet There are more variations on this, but none are good. It seems like the only way to reliably use `DiffUtil` with a large dataset and changeset is to either disable or queue all updates until `DiffResult.dispatchUpdatesTo` is called. Am I missing something that would make the above false?