Simple, Functional Async RecyclerView Diffing

Android’s DiffUtil
and its DiffResult
output have received lots of coverage, and all warn about how diff calculation is expensive and shouldn’t be done on the UI thread. Most examples go ahead to calculate the diff on the UI thread anyway, and for very good reason: The RecyclerView
is very tetchy about modifications to it’s state from a non UI thread.
The persnickety nature of the RecyclerView
has been detailed exhaustively by Jon Hancock here. I highly recommend reading that article as it’s where I found my feet. Further reading should include Erik Hellman’s “A nice combination of RxJava and DiffUtil”. With the information in those articles digested, we can start to think of a generic one shot approach to tackling diffing in RecyclerView
s regardless of List
or RecyclerView
Adapter
type.
Let’s start with some code. The star of the show is a simple interface, Differentiable
:
That is, in order to be able to be recognized as distinct, an item need only provide a unique String
identifier. All other callbacks are optional, lending to a semi functional interface. Coverage on them can be seen in the Android docs of DiffUtil.Callback
.
Next, let’s look at what the DiffCallback
for Differentiable
items would look like:
Finally, utilization of the above 2 to get a DiffResult
:
Let’s talk about that last bit some more and how it works. The first 2 arguments are Lists
of any type T
. The first is the original List
reflected by the current state of the RecyclerView
Adapter
. The second List
could be any non-null List
:
- It could be the new state the
RecyclerView
should reflect. - It could be a
List
of items to add to the existing list, perhaps theRecyclerView
endless scrolls and there’s new data to add. - It could be a
List
to filter the currentList
on.
… and so on. All that matters is that it’s a List
of the same type T
.
The third argument, the combiner, is the real implementation detail. It’s what combines the source List
, and the additions, subsequently returning a new List
that represents the new state of the RecyclerView
. The 3 lines of code in the method body show that regardless of the implementation of the combiner function, the entirety of the operation is pure; there are no side effects. The operands in the combiner function are copies of the source and addition Lists
. Moreover, the DiffCallback
makes yet another copy of the source so if the source were to change at some point later, the DiffResult
is a perfect snapshot of the two states the RecyclerView
has transitioned between should the result of the diff calculation be applied to the RecyclerView
.
The last argument, diffingFunction
, is a function that maps the type T
to a Differentiable
. As mentioned earlier, the easiest implementation is to return a unique String
id for each item. Provided the hashCode()
method is properly implemented for T
, using
item -> (Differentiable) String.valueOf(item.hashCode())
would suffice.
The result of the method, a Diff<T>
, contains the DiffResult
, and the new List
that would be accurately reflected via the application of said DiffResult
to the RecyclerView
, i.e a non generic Pair
. At this point nothing has really happened. The RecyclerView
’s Adapter hasn’t been notified, neither has the original List
changed, and that’s perfect because this calculation can be run on a background thread with no side effects whatsoever. As said before, the implementation is a pure function.
So how would one use this then? Consider a list of dancing tiles that change every 2 seconds. Using RxJava:
The makeNewTiles()
method is invoked every 2 seconds on a background thread, at which point a new tile Diff
is calculated. On the main thread, the List
backing a RecyclerView
is updated, and the DiffResult
is pushed to the View which will eventually call DiffResult.dispatchUpdatesTo(adapter)
. No IndexOutOfBoundsException
; inconsistency detected error to worry about.

For endless scrolling, the code looks like this:
Single from callable waits till the Single
is subscribed to, before generating new tiles and calculating the diff. Since the subscription is on a background thread, the diffing will also be done there. The combiner function simply adds the new items generated to the copy of the old ones then returns them. Back on the UI thread, the original list swaps its contents out for the result of the combiner and posts the DiffResult
to the hosting View
.

If you read Jon’s post, both these examples follow the queueing approach, as all diffing requests are handled by RxJava which queues them.
Coroutines would work just as well however. Wrap Diff.calculate
in a suspending function, and apply the the result when you’re back on the main thread, async diffing made simple.
The one caveat is the space complexity of diffing. There are 2 copies made of the original List
, and 1 copy of the additions. A small caveat for simplicity IMO. More importantly, this code lends itself to a plethora of use cases. I’ve used it for diffing endless scrolling lists, lists representing scanning for Bluetooth devices, to real time search results. Checkout the sample app below for a bunch of examples; including both described above. The endless scrolling list particularly, has been scrolled to 10,000 items using this diffing implementation with no issues.
Note: The AndroidX libraries does provide the ListAdapter
class to handle diffing for you internally, however, that requires delegating asynchronicity to the View
layer which makes me feel a bit uneasy. I prefer to keep my View
classes as simple as possible. Let the Repository
and ViewModel
classes handle the concurrency in the data layer. The View
should just be notified when it ought to change.
That’s all there is to it, reusable code to diff any RecyclerView
Adapter
backed by a List
of any type T
. You don’t have to create a different DiffCallback
for a different adapter
. You don’t even need to make your model objects implement Differentiable
, heck you could provide a different diffFunction
to diff 2 separate instances of the same type of Adapter
. It’s a simple functional approach to diffing, and there’s a lot to like.