-
Notifications
You must be signed in to change notification settings - Fork 727
Diffing
Epoxy is especially useful for screens that have many view types backed by a complex data structure. In these cases, data may be updated via network requests, asynchronous observables, user inputs, or other sources that would require you to update your models and notify the proper changes to the adapter.
Tracking all of these changes manually is difficult and adds significant overhead to do correctly. In these cases you can leverage Epoxy's automatic diffing to reduce the overhead, while also efficiently only updating the views that changed.
EpoxyController
classes use diffing automatically, and it can be optionally enabled in EpoxyAdapter
classes. See the documentation for those classes for details on how they handle diffing.
Epoxy calls hashCode
on each model to determine the current state of that model and to determine when a model has changed. It is important for each model to implement a hashCode method that correctly captures its entire state in order for diffing to work.
To avoid the manual overhead and boilerplate of implementing hashCode()
on all your models you may use the @ModelAttribute annotation on model fields to generate that code for you.
When using diffing there are a few performance pitfalls to be aware of.
First, diffing must process all models in your list, and so may affect performance for cases of more than hundreds of models. The diffing algorithm performs in linear time for most cases, but still must process all models in your list. Item moves are slow however, and in the worse case of shuffling all the models in the list the performance is (n^2)/2.
Second, each diff must recompute each model's hashcode in order to determine item changes. Avoid including unnecessary computation in your hashCode implementations as that can significantly slow down the diff.
Third, beware of changing model state unintentionally, such as with click listeners. For example, it is common to set a click listener on a model, which would then be set on a view when bound. An easy mistake here is using anonymous inner classes as click listeners, which would affect the model hashcode and require the view to be rebound when the model is updated or recreated. In this case you can use the DoNotHash
option (@EpoxyAttribute(DoNotHash)
to tell the generated model to exclude that field from the hashCode calculation. Another common mistake is modifying model state that affects the hashcode during a model's bind call.
A note about the algorithm - We are using a custom diffing algorithm that we wrote in house. The Android Support Library class DiffUtil
was released after we completed this work. We continue to use our original algorithm because in our tests it is roughly 35% faster than the DiffUtil. However, it does make some optimizations that use more memory than DiffUtil. We value the speed increase, but in the future may add the option to choose which algorithm is used.