Simple, Functional Async RecyclerView Diffing

TJ
ProAndroidDev
Published in
5 min readFeb 27, 2019

--

A List undergoing change from State A to B

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 RecyclerViews regardless of List or RecyclerView Adapter type.

Let’s start with some code. The star of the show is a simple interface, Differentiable:

Differentiable Interface

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:

A generic DiffUtilCallback that can diff any List<T> provided there’s a way to map type T to a Differentiable

Finally, utilization of the above 2 to get a DiffResult:

A simple method for calculating the diff between any list, on any thread

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:

  1. It could be the new state the RecyclerView should reflect.
  2. It could be a List of items to add to the existing list, perhaps the RecyclerView endless scrolls and there’s new data to add.
  3. It could be a List to filter the current List 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.

The Diff class

So how would one use this then? Consider a list of dancing tiles that change every 2 seconds. Using RxJava:

Diffing with dancing tiles

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.

Result of the dance method.

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.

Endless Scrolling with Diffing

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.

--

--