I have been using Cycle.js for a couple of small hobby projects and I really like the observables only approach. Having used reactive patterns on Android to some extent as well, I was wondering if a similar architecture could be achieved in an Android application. The release of the new Architecture Components from Google finally presented an opportunity to try and re-write and old application of mine with (almost) observables only.

This blog post will focus entirely on my take on how to implement certain ideas from Cycle.js in Android. I will not spend too much time on explaining the patterns themselves. Please check out the documentation on Cyclejs.org if you are not familiar with it. Besides Cycle.js, my approach is also inspired by the MVI series of Hannes Dorfmann and Jake Wharton’s talk on the state of managing state with RxJava.

Application

When I try out new patterns, I usually start off with implementing them for very simple use-cases, but then quickly move on to applying them to something a little more complex. I think you can only really evaluate a certain pattern once you get to the ugly details of a real world application and see how it behaves in those scenarios.

Hence the application I am talking about today features many aspects of a real world Android app: it fetches data from online sources, stores them in a local database and presents it with an overview and a details screen. Although still being relatively simple, I think it serves well as a test-case for a new architectural pattern.

The app features two screens: the first one uses data from TheMovieDb to show movie posters in a grid, either sorted by popularity, rating, date or starred by the user. The second screen shows detail information about a movie. The user can star the it, which saves it to the local SQLite database and thus makes it available offline.

The full source code of the application is available on github.

Approach

The basic idea is that all the business logic of the application lives in top level functions, detached from the Android framework. These functions set up an observable stream that emits the current state of the screen. In order to keep this stream alive across configuration changes, it is created in a retained ViewModel from the Architecture Components. The ViewModel offers this state-emitting stream as LiveData to the Activity/Fragment, whose job is to transform the state into something on the screen. Using LiveData is nice because it is lifecycle aware and always replays the latest emission to a new observer. The same could be achieved with pure RxJava, LiveData is just convenient.

Code

I started using Kotlin a couple of months ago and what can I say, it is awesome! If you have not used it yet, I really encourage you to give it a try! Besides Kotlin, the application is heavily based on RxJava and uses the new Architecture Components from Google, namely ViewModel, Room and LiveData. The local SQLite database is created using Room and the REST calls to TheMovieDB use Retrofit.

The code is organized within two main feature packages, grid and a details. Within those, all the top level business logic functions are contained in a component package. This clearly separates them from the code dealing with the Android framework.

The observable streams are modeled using the Model-View-Intent terminology from Cycle.js: every component has intention (intent is kind of already taken in Android…) and model functions and returns a state-emitting Observable. Yes, the V is missing. Due to the way the Android framework is currently built, using a view function like in Cycle.js is difficult. Instead the Activity/Fragment subscribes to the state stream and renders each emission on the screen.

Component

Every component features a main function. It takes as input a sources object that contains — as the name implies — all possible sources of data for the stream. This includes abstractions for getting data from the network, the local database and shared preferences. Lastly and most importantly the sources include all the UI events, e.g. the user clicking on a movie poster or scrolling down to load more data.

Note the usage of Relays here. As the stream is kept alive across configuration changes, the views cannot be directly bound to it, as they are recreated after a configuration change.

Intention

The intention function transforms these UI events into actions, modeled in a sealed class. This provides a nice abstraction for the model function, as it does not have to deal with UI events directly, but simply receives a stream of actions. This is also useful for testing as you will see later on. A shortened version of the grid intention function portrays the idea.

Model

The model function takes these actions as input, acts accordingly and produces a new state. It does so not by directly mutating the current state but by creating a new one every time and incorporating changes using reducer functions. If you have used Redux or cycle-onionify on the web, this pattern will look very familiar to you. Again using a shortened version of the grid model function to illustrate:

A quick note on navigation: instead of putting such actions directly into the state object (e.g. a showDetailsFragment: Boolean), I opted to have a separate stream for them. This frees me from having to emit events like detailsFragmentShown. The Activity subscribes to this navigation stream as it does to the state stream and acts accordingly on every emission.

Note that an approach following Cycle.js more purely would take this further and create so-called drivers not only for navigation, but for everything that performs a side-effect with the outside world. You could do this here as well and remove HTTP or SQL requests from the main model stream. Instead you would create separate streams for every effect and feed the results as an additional source into the main model stream. This makes testing a bit easier as you would not need to mock anything to test the main model stream. For more information on this driver pattern, please visit the Cycle.js documentation: https://cycle.js.org/drivers.html

Rendering

The Activity/Fragment observes the LiveData from the ViewModel and on every emission updates the view with latest state. This update happens via the DataBinding library, using something I call a ViewDataObject. These are mutable and bound to the views. Whenever the Activity/Fragment observers a new state emission, it changes the fields in the corresponding ViewDataObject and the views update automatically thanks to DataBinding. Instead of using DataBinding, you could of course also directly set the values on the view. DataBinding ist just convenient because it handles things like not setting a new value on a view if it is identical to the old one.

For the navigation stream, using LiveData is not appropriate because you obviously do not want the replay-latest feature in this case. Thus I am using plain RxJava. This requires some manual lifecycle handling, but that is actually super easy to do with the LifecycleRegistry and a Kotlin extension function:

Testing

As all the core business logic is contained in top level functions that take a stream of observables as input and output a stream of observables, testing becomes relatively simple. You only need to mock the service that fetches movies online from TheMovieDB and the DAOs accessing the local SQLite database. I opted to separately test the model, intention and navigation functions of each component. An example test from the grid component’s model function looks like the following (some class properties and helper functions are omitted, but you will get the idea):

Head over to the github repo to see the complete test classes. Note that the details component is currently lacking tests, they will follow.