Friday, July 17, 2015

Android Data Binding: Goodbye Presenter, Hello ViewModel!

The Model-View-Presenter-pattern (MVP) has been the dominating trend lately when it comes the UI-layer architecture of Android applications. Frameworks like Ted Mosby, Nucleus and Mortar have all talked about Presenters to help you achieving a clean architecture of your app. They also (to a variyng degree) help you with the infamous issues of device rotation and state persistence on the Android platform. This isn't directly related to the concept of MVP, but the pattern helps you isolate the boiler plate code.

Data binding, as announced on Google I/O 2015 and introduced in the Android M preview, changes everything. According to the Wikipedia article on MVP, the Presenter has the following tasks:
The presenter acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view.
The thing is that Data Binding framework will take over the main responsibilities of the Presenter ("acting upon the model and the view"), while the remainder is left to the enhanced Model - the ViewModel ("retreiving data from repositories and formatting"). The ViewModel is a standard Java class whose sole responsibility is to represent the data behind a single View. It can merge data from multiple sources (Models) and prepare that data for presentation. I did a short writeup on the ViewModel and how it differs from Data Model or Transport Model.

The architecture we end up with is MVVM - Model-View-ViewModel, and is a proven concept originally coined by Microsoft back in 2005 (don't let that scare you ;-) ). Let me illustrate the change from MVP to MVVM for you, shamelessly copying Hanne Dorfmann's illustration from the introduction of his Ted Mosby framework

mvp
mvvm

So all the binding and updating of data to the view is done through the Data Binding Framework. The ObservableField class allow the View to react on changes to the model, and the XML references to fields allow the framework to push changes back to the ViewModel when the user acts upon the View. You can also programatically subscribe to changes in fields so that for instance a TextView is disabled when a CheckBox is clicked. One great advantage of representing the visual state of the View in a standard Java class like this is clear: You can easily unit test the visual behaviour.

Note that in the MVP illustration above there is a method call to Presenter.loadUsers(). This is a Command. In MVVM these are methods defined in the ViewModel. From the Wikipedia article:
The view model is an abstraction of the view that exposes public properties and commands
So this may or may not be a change to what you're used to. In the MVP pattern it's likely that your models were "dumb" classes only holding data. Don't be afraid of putting business logic in your Models or View Models as well. This is a core principle of Object Oriented Programming. So back to Presenter.loadUsers() - this will now be a method in the ViewModel and may be invoked programmatically by the code-behind* of your View or though a data bound command in the XML of your View. That is - if the promises of this issue in the android-developer-preview issue tracker hold up. If we don't get data binding to commands, we have to resort to the old android:onClick-syntax, or manually adding listeners in the View code as before.

*) "Code-behind" is a term from Microsoft, often with negative associations to early ASP.NET or WinForms. I think it's a describing term also on Android where the View is composed of two source elements: the View Layout (XML) and the Code-Behind (Java), represented by Fragments, Activities and classes extending View.java.

Dealing with system calls

There is one set of use cases which still have to be done in the code-behind of the View - functions which initate system calls, opens dialogs or basically any call which require reference the Context object of Android. Don't put code like this in the ViewModel. If it contains the line import android.content.Context;, you're doing it wrong. Don't do it. Kittens die.

I haven't quite made up my mind on the best approach to tackle this yet, but that's because there are several good options. One way would be to keep elements of the presenter concept from Mosby by referencing an interface to the View in the ViewModel. This way you won't reduce the testability. But instead of having a seperate Presenter class as in Mosby, I'd stick to the View as the concrete implementation of that interface just to keep it simple. Another approach could be to use an event bus like Square's Otto to initiate commands like new ShowToastMessage("hello world"). This will yield a greater separation of the view and the viewmodel - but is that a good thing?

Don't we need frameworks now?

So is the Data Binding framework taking over the job of framworks like Mosby or Mortar? Only partly. What I hope to see is these frameworks evolve or fork into MVVM-style frameworks so that we can leverage the best of Data Binding while keeping the dependencies to 3rd-party frameworks to a minimum, and keeping the frameworks small and simple. While the era of the Presenter might be over, the framworks do a just as important job with lifecycle management and view state (ViewModel) persistence. This has not changed (unfortunately*)

*) Wouldn't it be cool if Google introduced an interface LifeCycleAffected which Fragment, Activity and View implemented? And that interface had methods named addOnPauseListener() and addOnResumeListener()? I leave it up to you to what that might have done with our code base, and the code base of MVP/MVVM frameworks.


Summary

When I first heard that Android M was all about improving the SDK and focusing on the developers, I was really excited. When I heard they were introducing Data Binding, I was stoked. I have worked for years with data binding on other platforms : WinForms, WPF, Silverlight and Windows Phone. I know it will help us write cleaner architecture and less boiler plate code. The framework works with us instead of against us, as I've felt it has been for a long time.

But it's not a silver bullet - there are downsides. The fact that you define stuff in XML is an issue. XML is not compiled. XML cannot be unit tested. Hence you'll often end up noticing errors run-time instead of compile time. Forgot to bind the field to the view? Tough luck. But the tooling can help a great deal here - that's why I hope Google is focusing hard on making Android Studio support data binding to the max. Syntax and reference checking of the binding XML, auto complete and navigation support. Field renaming support which propagates to XML. From my testing of Android Studio 1.3 beta - I'm at least assured that the are thinking about it. Some things are supported. A lot isn't, but we're still in beta so here's hoping.

Code example

I've posted an example on github where I try to illustrate the conseqeunces of going from MVP to an MVVM architecture. I used Mosby as a framework in the MVP version with Butterknife for view injection. In the MVVM example I'm using Android M databinding and drop dependencis to both Mosby and Butterknife. The result is that the Presenter can be dropped, the Fragment gets less code but the ViewModel takes over a lot of the code. In some sense it actually acts partly as a presenter, in that it directly or indirectly notifies the view.

The example is a login screen with som async data loading and inter-view dependencies just for illustration purposes.

illustration

Checkout the MVP tag to see the MVP example and the MVVM for Databound MVVM

No comments:

Post a Comment