Flux Android Architecture Components using Kotlin

Lewis Rhine
LewisRhine
Published in
8 min readJun 6, 2017

Like every Android Dev, I was over the moon when I saw that Google announced Kotlin as a first class language at I/O. That said, the thing that sparked my interest even more was Android Architecture Components.

My first thought was “Oh god no, Google’s opinion of clean architecture! This is going to be so needlessly over-complicated”.

After seeing it though, I thought, “Oh, that’s not so bad. I love how they are breaking it into components”

“Oh nice, I’m glad they are saying this is NOT how it SHOULD be and this is just their take on it”

“Oh God, It doesn’t matter that Google says that, every manager who says the project must use fragments because ‘Google says so’ will say the same thing about this”

“Oh no! I just remember data binding is still a thing!”

After waking up from a “code in XML” induced blackout I started to think, “Since there is a good chance that these components are going to become the ‘that’s just how it’s done’ of Android architecture and be adopted as gospel in most projects, I wonder how easily someone could implement a Flux architecture using them”. So I decided to clone the sample app and give it a go.

If you are not familiar with Flux and Redux here is a quick TL;DR on them:

Flux is an architect that is based around the idea of unidirectional data flow, meaning the view layer spits out actions. Your data layer and logic layer listen for those actions, add data and/or do some business logic on it and then pass it back to the view layer. It’s one big happy circle.

Flux flow diagram.. more or less.

Redux added the idea of having a single immutable state object and a pure function called a reducer that takes your apps current state object and action and returns a new state object. The immutable state keeps your app state very clean and clear while the reducer functions make testing your app extremely easy

Redux flow diagram again, more or less.

There is much more to those two. I encourage you to read up on them but for now that is really all you need to know.

To start off I am going with the BasicSample project, you know because it’s basic. All its doing is showing a list of products (weird shit like Rubber Chickens and Monocle ¯\_(ツ)_/¯) it gets from a database and when you click on one it shows a details screen with comments about that product.

First things first I add Kotlin so I can build a State Tree using a Sealed Class. (for more info). This is going to be our Redux style immutable state object unlike Redux, however, I don’t want a single app-wide state object in my opinion the state should be per screen. Otherwise, it feels like we are fighting the framework too much. I might hate fragments but I do still like the ideas behind Activities.

This is what the ProductListStateTree looks like

These are all the state I can foresee as of now this screen being in.

What I want to do is treat the ViewModel as the Flux Store. Just like with the state object I am going to break from the Redux implantation and not have a single Store for the whole app but instead one for each screen. While I hate the name ViewModel (to me it drips with Microsoft-ness) and it no longer fits since we are moving away from MVVM. I do however also think it is overall better to have a universal name and since after all, what’s in a name, might as well go with this one.

The ViewModel should only expose a single LiveData field of our state tree from which out our UI, in this case, ProductListFragment, will update its view state and get all data needed data.

Before we do anything else let’s create our Action Creator. Remember how I said that with Flux all events that would cause a state change, like the user clicking on something, is an action? Well, the Action Creator is how we forward all that throughout the app.

Pretty simple, nothing more than a single object implantation of a MutableLiveData class with a dispatch function that just serves as kind of a way of making more clear that you are dispatching actions but if you wanted you could just call setValue.

Under it, we’re using a sealed class again (I love these little bastards) to build out the actions our app can have, which as of right now are just GetProductList and GotProductList. In the JavaScript world, Flux Actions are just described as an object that has a type and a payload. But we don’t live in this mad world of anything and everything goes. We gladly wrap ourselves in type-safety blankets. They are warm and gentle, you scripting language lunatics.

We can now update the ProductListFragment class it to use our state tree and action creator.

Once the view is ready we dispatch a GetProductList action. The view does not know or care what that means to the rest of the system, just that when it loads it’s saying “hey I need a list of products”. We also added an instance of check on the productListStateTree object we are given from the ViewModel to know what state the UI should be in. This looks ugly in Java but that’s not how we are meant to work with these sealed classes. While it does work it is kind of an eyesore and we are missing out on the power of the Kotlin when statement.

There we go, now we just check each type we care about in our when statement and hide, show, and set our views as needed.

Now the fun starts with the ViewModel. First, we need to add a reducer function. This is one of the fancy pants things from Redux. It’s going to be a pure function meaning there are no side effects. Think math functions, say for example you give it an input of five, it does some magic inside the function and gives an output of fifteen, for it to be a pure function it must always always always give you a fifteen when you give it a five. That means we don’t want to do any database or web calls in here as those can have effects outside the scope of the function. The reducer gets its data from the action it is passed. Based on that action; any data it might carry, and the current state tree it will return a new state. Here is the function ProductListViewModel will be using.

So we are saying that if a GetProductList action is called we want to update the state to ProductListStateTree.Loading unless that’s what the currentState is already is then we just pass the old status along. The if we get a GotProductList action we check if the list is empty or not and update the state accordingly.

Hopefully, something jumps out at you here. We are not accounting for all state ProductListStateTree can be in. We are not handling for an error state. For now, it’s fine because it’s just a basic sample but that shows one of the benefits of building out the state tree. It helps us keep all the state in mind.

The other thing that probably jumps out is “Oooookkkk, but why? And how do we get the data into the action and into this wacko function? What is wrong with you?” Firstly, a lot of things are wrong with me but this is not the place for that and you are mean. The reason for doing this is this function becomes a place to put all of your important business logic that it is extremely easy to unit test. If you want you can ignore unit testing the bulk of your code and focus on just the messy stuff, the stuff the really should have a unit test around it. And because we are putting all that mess inside a pure function mocking and assessing because insanely simple.

Now as for getting the data into the action. Redux solves this with Middleware that intercepts an action, in this case, GetProductList call out to get the data than then dispatch a new action with the data, in this case, GotProductList. Since we are stepping away from the one store in Redux it seems like we want to have the ViewModel implement all middleware. Personally, I am not sure where the middleware should go for sure and that is something I would love feedback on. But for now, we’ll do it in the ViewModel the similar to how did the reducer.

Our middleware function is taking an action and any sources it would need to get data in this case the project is using Room to interact with the database. Ignoring that fact that in their Guide to App Architecture they suggest using a repository for getting and storing data they are not doing that here. I guess this sample is “so basic, it can’t even”. The import thing we pass it to the function. Like the reducer, it grabs any action it cares about. Unlike the reducer it is making outside calls to get the data and is therefore not a pure function. This makes it a bit harder to write unit tests for but since it is taking the (not a repository) data source in as a parameter it’s still a simple matter of handing it a mock version of that object.

Last things last, let’s put all this together in our ProductListViewModel class.

Pretty simple, our state LiveData is the only data we expose. It is running through a switchMap that takes any action from the Action creator and runs them into our middleware function then using a MediatorLiveData object we run its return through our reducer. If the new state we get back from the reducer is different than our current state we update the current state; add it to our state history (more on this later), and then set the value which will update anyone who is subscribed to it. The reason for keeping a history of all the state changes is one of the big benefits of using a state object. It allows for easily tracking a logging the whole chain of events when debugging and can even be used to “time travel” as it’s called in Redux. Since your UI gets its data and sets its views from these state objects if you have a list of states you can basically undo/redo the states of your app by sending these object from your history map.

This is just a first step but after doing it feel like it has promise. I will continue to convert the rest of this basic bitch and move on to the more advanced examples in the sample project. If I’m happy with it I will probably rewrite Akorn to use it. Then later when I have some personal stuff figured out (Hey you’re the one who asked) try it inside a personal project. If you would like to me keep writing about my journey down this unidirectional hole let me know in the comments.

--

--