ThirtyInch —a new MVP library for Android
Most Android Developer know that the best practices from Google for Android aren’t very practical. They are still promoting AsyncTask as the solution pulling work from the main thread. I found myself implementing SyncAdapters and ContentProviders because that’s what the Google I/O app does for downloading and saving data in a database; completely over engineered from todays perspective. Following Googles advices isn’t always the best option.
living next to the Activity
Most people got this and move away from putting all the logic into Activities, Fragments and Services. Dianne Hackborn from the Android Framework Team wrote a wonderful article that there is no recommended architecture for Android Applications. The Framework Team doesn’t care how you organize your app logic as long as you respect the high level contract of the Activity. This freedom allows us to use well researched design patterns which are around for decades. Recently, MVP has become a very popular architectural pattern in the Android community, MVC and MVVM are used, too. They all have in common that they move business logic out of the Activity.
At grandcentrix testing has a very important role. We started with InstrumentationTests, wrote plain UnitTests where possible. A lot of logic still lived in our Activities and we tried to test this logic with Espresso tests; the journey most Android Devs experienced. We ended up with a lot of flaky androidTest tests which randomly fail even when they worked for months.
flaky tests are worse than no tests
New projects have been started and we agreed that we’ll skip androidTests on the emulator as much as possible. We’ll move our logic into testable components which can be tested on the JVM rather than an emulator. Where it makes sense we still do Espresso testing.
We did not agree on an architecture for our apps following our mantra “The right tool for the job”, but we all drifted towards an MVP architecture and ended up with at least 3 different AbstractPresenter implementations. It was time for a library combining the best ideas of all implementations.
ThirtyInch — the MVP library
The library implementation has started even before Ha Do released his Mosby library which was one of the first MVP libraries for Android. I really recommend reading his articles about MVP if your aren’t familiar with the pattern. The concept of the passive View also shaped this project.
A hand full of good other MVP libraries have been published over time but they could not replace our internal implementation because of missing features or too many dependencies. So we made the effort and prepared the library for a public audience.
What is ThirtyInch:
- The core implementation is very lightweight giving you a TiPresenter with a TiView interface which can be attached or detached.
- The TiPresenter can be described as an Object which lives during the whole lifetime of an Activity, even when the Activity gets recreated, moved into background and gets killed. When the Activity finishes the TiPresenter dies, too (onDestroy()).
- The TiPresenter has four lifecycle events.
onCreate(): called once for the initialization, the view is currently not attached
onAttachView(view): the View was attached and is visible to the user
onDetachView(): the View will be detached after this call and is not visible to the user anymore
onDestroy(): called once when the Activity/Fragment is completely destroyed and will never return again. Stop all your work!
onAttachView(view) and onDetachView() are mapped to onStart() and onStop(), callbacks for onResume/onPause are not supported because those lifecycle events should be handled in the View layer.
- The TiPresenter is able to survive orientation changes allowing you to keep network requests running and hold your Model instead of serializing it in onSaveInstanceState.
- The TiPresenter can be attached to Activities and Fragments
What makes ThirtyInch different:
- Configurable for your needs. You can pass a TiConfiguration into a TiPresenter removing features such as surviving a configuration change or the View method annotations which are enabled by default. When you remove everything you end up with nothing more than the Mosby library.
- All Presenter lifecycle events will be called in the correct order and onCreate() and onDestroy() only once. This may sound relatively easy but naive MVP implementations can detach the View twice leading to crashes or unnecessary null checks.
- No RxJava dependency. Some MVP libraries have a dependency to RxJava even if you’re not using it in your project. ThirtyInch has a separate Rx module you can add optionally allowing you to manage subscriptions and delay events until the View is attached. ThirtyInch also was RxJava driven and this separation was one of the main reasons why it hasn’t been published earlier.
- View interface annotations for methods (configurable with TiConfiguraiton):
@CallOnMainThread makes sure the View method will be automatically called on the main thread
@DistinctUntilChanged doesn’t call the View method twice when already called with the same parameter(s)
The annotations work by proxying the View.
- Public API. The Rx module relies on the TiLifecycleObserver and the annotations use the BindViewInterceptor API. These and all other APIs are public and can be used by everyone making your TiPresenter even more powerful without forking the library.
- You don’t have to extend TiActivity and increase the inheritance stack of your BaseActivity. You can use CompositeAndroid and add the TiActivityPlugin to your Activity by adding the plugin module to your dependencies.
Before diving into the code I’d like to give an overview how the MVP architecture gets implemented with ThirtyInch.
This is what a ThirtyInch Hello World project looks like:
Not strict MVP, it also works with MVVM
ThirtyInch is a MVP library focusing on the Presenter and doing all the heavy Android lifecycle stuff for you. The View is an empty interface and the Model is totally up to you. Using a ViewModel is completely fine and a good pattern handling complex UIs.
A ViewModel saves the current state of UI elements such as the visibility or the enabled state in an abstract way, using boolean fields or POJOs instead of android.view.* properties. Whenever my ViewModel changes I bind the ViewModel to my View doing the React approach: Rebuild everything and only apply changes. Even for the tiniest change.
This binding process calls all View methods like setLoginButtonEnabled(Boolean). The same binding process is used when the View was attached for the first time (onAttachView(view)). This can be expensive for methods resulting in heavy layout changes i.e. showTweets(List<Tweet>) which updates a RecyclerView. But thanks to a the @DistinctUntilChanged annotation methods will only be called when the data changes. Calling the method twice with the same list of tweets doesn’t update the Adapter and therefore doesn’t redraw the UI.
Maybe we should call this VPVM — View Presenter ViewModel
Testing — Keep Android At Arm’s Length
The reason why people move code out of Activities is driven by the demand to write maintain- and testable code. Especially testing is hard due to a lot of final methods and private classes in the Android SDK which can’t be mocked in androidTest and have no implementation in the test environment. That’s why I try to keep my Presenters Android free. My Presenters don’t know about Activities or a Context. All they know is a View interface containing methods like openUserDetail(User).
This View interface could be implemented by a Activity or a terminal application. The Presenter doesn’t care. I always have two implementations of the View interface, the Activity extending the View and a mock implementation for testing. As the Presenter, the mock View implementation has no relation to Android classes, allowing me to write fast JVM tests.
If you have use a Context aware Object like a GpsService in your Presenter use dependency injection and make sure you can mock it in your tests. Beware that you should never ever put the Activity, or a Object holding the Activity into the constructor of the Presenter. The Presenter lives longer than the Activity and would leak it.
That’s meant with “Keep Android At Arm’s Length”.
Don’t combine Android and business logic code. This principle lead to the controversial library name ThirtyInch — the average length of the human arm, shoulder to fingertips.
How does the Presenter survive the configuration change?
There is one thing about the Activity lifecycle I simply don’t get: The recreation of the Activity instance when the Activity receives a configuration change such as the orientation change. People with a web development background can’t get it in their heads. A website doesn’t reload when the size changes.
As a result of this recreation, your new Activity instance doesn’t know about the state of the old Activity. You lose your current selected tab if you don’t serialize this information. Network calls, which cannot be serialized are even harder and most apps simply cancel them. Or they don’t cancel but ignore the result when the old Activity got destroyed and start a second request.
Serializing data is time consuming and the serialization of big data increases the duration of the recreation process. Not to mention the ugly and error prone Parcelable implementation. Why would I want to serialize data anyways which I immediately deserialize a few milliseconds later in the new Activity Instance?
The Android Framework has two ways to work around the unnecessary serialization:
- A Fragment with setRetainInstance(true) will be restored in the new Activity instance. The same Fragment instance we had before with all properties and all references to long running tasks.
- Use Activity#onRetainNonConfigurationInstance() to save a random Object and get it in the new Activity instance with Activity#getLastNonConfigurationInstance(). That’s exactly how the Android Framework saves retained Fragments.
This method was recently undeprecated. The deprecation before was in favor of retained Fragments which always used this deprecated method.
The naive Java solution would be a Singleton which lives forever when the app is not killed.
ThirtyInch uses all three methods (configurable) to make sure the TiPresenter will survive configuration changes. The Singleton solution is required for some edge cases but Presenters will never be leaked.
When I use a TiPresenter I don’t implement Activity#onSaveInstanceState(Bundle) anymore. I hold the data in my Model (POJO) in my Presenter, no serialization required. It’s faster and I’m confident that todays phones with gigabytes of RAM can handle a few kb extra in memory when my Activities are destroyed. We should remember that Android was developed for phones with 200mb RAM.
We’ve build a MVP library called ThirtyInch with a Presenter for Activities and Fragments which survives orientation changes. The library has grown 16 months internally and is now available to everyone.
TiPresenter lifecycle callbacks according to the upcoming 0.8.0 release.
Give it a try and write your next Activity with ThirtyInch. I’m excited to get your honest feedback!
This was just the introduction of ThirtyInch and more detailed posts are planned. Follow grandcentrix to get further updates and check out our job listings if you are interested.