Android.apply{ Redux }

Edward and Francesco
6 min readAug 22, 2017

TL; DR:

After years of happy MVP-ing we were intrigued by the react pattern which is gaining more and more traction in the Android community. We saw videos & posts like Redux-ing UI bugs to Make Native Better by Christina Lee and @bkase_, The State of Managing State with RxJava by Jake Wharton and Reactive apps with model-view-intent by Hannes Dorfmann talking about unidirectional data flow and patterns like redux inherited from the JavaScript community.

That sounded extremely interesting to us so we decided to give it a go. This is our understanding and interpretation of such an architecture for Android.

In this post we are showing the basics and why we think a pattern like this is good for Android development.

Keep on reading if you are interested in:

  • A non trivial example: we show how to lazy load a list of items, each of which loads details when on screen.
  • Usage of Android Architecure Component’s ViewModel and LifecycleObserver
  • 100% Kotlin code
  • A fully tested example

We are going to write more around our usage of Architecure Components, Kotlin and tests.

The code! → https://github.com/shazam/android-redux-sample

Why leave the good old faithful MVP?

In MVP (or at least, in our vision of MVP) it is only the view that holds the current state of itself. Think about a screen that shows a “Loading….” spinner: the Presenter sets such spinner to visible, and once the requested data is loaded the spinner is hidden. But what happens if, in the meantime, the presenter wants to know if the spinner is visible?

It can either privately keep this information or query the view for it.

Now, what if the screen has more and more features? Is the presenter going to keep the current “state” of every single button, view, list along with the business logic of the app? Well, if yes: the complexity to keep synchronised what the view is showing and what the presenter knows about the view will easily explode.

Because an Android View is a very transient object, that could be destroyed at any given moment, and because the View is the state, it is hard to keep track and recreate exactly the last screen visualized to the user.

Sure, we could save states, provide such states to the presenter, have the presenter re-start the journey to dictate to various parts of the view what to do.

But what if, instead, we remove the concept of keeping the view state from the view and the presenter (and we will see how in a bit!)? It will be a matter of creating a ViewState, keeping the latest known state handy… in case the view is destroyed and recreated... and provide it to the View!

Alternatives to MVP?

There are plenty of alternatives, for example a pattern that could solve some of the mentioned problems, and has existed for decades, is called MVVM. But unfortunately every coin has two sides and MVVM has its own pitfalls.

More recently the javascript world brought back the attention to circular unidirectional data flow. The concept is actually very intriguing and promises to solve some drawbacks without adding too many issues of its own. That’s what we decided to explore.

Requirements:

We want a solid solution that is:

  • Easy to implement
  • Easy to test
  • Easy to debug in case of problems
  • Easy to extend or modify when necessary

Finally: our solution! :)

We ended up with something that is inspired by redux and flux. We have a Store which accepts Actions and uses a Reducer to turn that into State. We also added a Dispatcher to sit between the View and Store in order to act as a mediator between the two. We’ll explain each part in more detail shortly.

Overall our architecture works like this:

→ a click on the “Refresh” button is performed and sent to the Dispatcher

→ the Dispatcher calls Middleware to perform I/O that dispatches a new Action (now enriched with some of the data to be displayed on screen) into the Store

→ the Store (that is pretty much just holding the latest known State and the Reducer) provides the just calculated Action to the Reducer

→ the Reducer merges the latest know State with the new Action and emits a new State

→ such State is both kept in memory in the Store for the next iteration and also provided to the View to be shown to the user.

The details

Actions

Actions describe what changed. They are events like “loading data started”, “data loaded successfully” or “internet connection lost”. These actions are just plain, immutable objects. It could even be a Kotlin object if there is no data associated with the action, or a data class if it has data.

State

The state is what describes the view at a single moment in time. The view should be observing changes in state and updating itself accordingly. The state is an object, just a simple POKO and should be immutable.

Reducer

The Reducer is what applies updates to the State. It is no more than a pure function which takes the old State, an Action and returns the new State. It is the job of the Reducer to turn Actions into State. For example if we receive a ‘loading started’ action we would flip the loading flag in the State. Because the Reducer is a pure function, only acting on its inputs, it makes it really easy to test. Furthermore, because the reducer is the only place that is mutating state, if you see unexpected state, you know where to fix it. This means much less time hunting for the source of bugs.

Store

The Store is what holds onto the current State. It is the single source of truth for what is being displayed on the UI. State can be accessed through the Store either by asking for the current state right now, or observing changes in state through a listener/RX observable. The Store is also responsible for updating the State. Actions are dispatched to the Store and, using the Reducer, the State is changed. This, in turn, calls the listeners.

So, putting these three together we have changes in our application dispatched as Actions, which are Reduced into State which can be displayed in the view. What this gives is a unidirectional flow of data which is easier to manage and reason with.

We went a little further to add some consistency to how we would be dispatching Actions and fetching data. We created a Dispatcher to act as glue between the UI and the store, and a Middleware to perform async operations (eg. api call, I/O) that transform ui events into Actions, dispatched to the Store.

Dispatcher

This is the glue between the UI and the Store. When the user clicks a button in the UI, this calls the Dispatcher. The Dispatcher will call the appropriate middleware function (which might be fetching data from the network), that emits Actions. These Actions are dispatched to the Store.

Middleware

The middleware transform Actions into Actions. This is where, for example, an UI-action is transformed (through API call, db queries, biz logic, etc) into another one containing refined data to be provided to the Store.

We can even have a chain of middleware(s) calls since each one is accepting actions and emitting actions.

Conclusion

We feel that this approach allows us to better manage the state within our app. Because all state changes go through the reducer we can easily test and debug. All changes are represented as actions and flow through a single point into the store.

This is our experience from an internal investigation and we are not using this pattern in production yet. We personally think it’s extremely promising and hopefully, in the future, we’ll use it for production code.

Stay tuned for more posts about how we tested our code, used arch components and leveraged Kotlin!

Resources:

--

--