Model Binding for Android
There are tons of approaches how to update UI when data changes or other way round, short Model binding. In this post we will investigate four possible implementations of a one-way binding and compare their performance in a series of tests. Different code snippets provide an overview of the complexity when applying the approaches to a real example.
Context
While developing the Android Client for a platform that provides near-realtime experience for interactions between users,
we had to implement a binding between the Model and the UI. During the development we created and evolved our own system,
but we ended up with something very similar to Java Bean’s PropertyChangeListener.
To find a better approach, I created a small setup consisting of a model “Profile” and a UI reflecting a profile’s properties
(such as name, age, followers). Inside this setup I implemented the different solutions and trialed them in a predefined
set of tests.
Unfortunately I didn’t find time to finish a version with an Eventbus such as Square’s Ottobus
, I may add this in the future.
Approaches
Overview
- Original: Project’s implementation as-is
- PropertyChangeListener: the java.beans solution + a modified version that notifies on UI-Thread
- Property Class: Properties are replaced by a Wrapper-class with Observable-feature
- Proxy solution: Java’s dynamic proxies is used for notification in a AOP-like manner
Project’s solution
Our own solution is very similar to PropertyChangeListener, a FieldObserver can directly bind to a FieldObservable Model’s Field.
FieldObserver and Field are generic and provide type-safety.
Further it provides Thread-safety and updates the Observers directly on the UIThread which allows us to make updates from
any thread.
The disadvantages are the huge boilerplate when creating a new model, as well as
the high complexity of the implementation. Therefore this implementation needs some serious refactoring, which will
probably never happen, since the app is not under active development anymore.
Let’s take a look how we implement the Profile:
As you can see, we were using an abstract class to inherit the common functions. Refactoring this to a component would make Profile implementation look like the next one, PropertyChangeListener.
PropertyChangeListener
This implementation is based on Java Bean’s PropertyChangeListener. There are basically two versions under test, the original one and a slightly modified one. In the modified version the Listeners are notified on the UI-Thread which allows us to call the model’s update-method from a background thread.
Profile implementation looks like this:
Property Class
The property class is a generic class, that holds its own listener. The Observer pattern is therefore moved from the model to its properties.
Profile implementation now gets really comfortable:
Done. This is a very solid solution and let’s you implement new models lighting fast. The disadvantage of this solution may be the high number of small object that are produced, which shouldn’t be a problem in most context. Further it is hard to acquire information about who is listening to a model, since the listeners are spread over the model’s property
Dynamic Proxy solution
The most advanced and interesting solution includes the usage of Java’s dynamic proxy. A similar approach is also used by
the widespread Spring framework.
The dynamic proxy stores properties according to the called “set”-method in a Map. Therefore “setFirstname” will store
the provided as value of the key “firstname”. Further it implements “get” and “is”, as well as addObserver. RemoveObserver
is not implemented, since it is not used by the tests. The full code of the Proxy can be found
This is the interface of Profile:
Creation of a new Profile will in this case look as follow:
Note: To use Dynamic Proxy on Android, the ClassLoader must be obtained via a context: context.getClassLoader()
For more information see also “Java Reflection - Dynamic Proxies” by Jakob Jenkov
Comparison
All test are run on a LG Nexus 4. There are two profile models that are modified during the test.
The time measurement
ends when all changes have been applied (using Timelatch).
The times below represent the average time per set and is calculated by:
(Total time) / (number of setter-calls)
UI-Thread: 24’000 setter-calls
Singlethread: 24’000 setter-calls in background thread
Multithread: 3 Threads à 8’000 setter-calls
Approach | UI-Thread | Multithread | GC runs |
---|---|---|---|
Project solution | 28 μs | 40 μs | 20 |
PropertyChangeListener | 38 μs | 23 | |
PropertyChangeListener (mod) | 31 μs | 44 μs | 23 |
Property Class | 33 μs | 40 μs | 20 |
Dynamic Proxy | 85 μs | 93 μs | 45 |
Conclusion
Please note the performance penalty of multithreaded runs, which are caused by the low priority of background threads. The custom implementation seems to have a slight performance advantage. But the differences are small. Only dynamic proxy takes significantly more time and causes more GC-runs than the other solutions. I suspect the overhead of the reflective method lookup causes this performance cost.
From implementation perspective the Property Class is the easiest to implement and use. But the fact that observers
are stored for every property can be a big disadvantage.
The Dynamic Proxy provides also an easy-to-use solution, but comes with a overhead on runtime. In our case this is the
solution I would go today, because it gives us further possibility to interfere interaction with the model.