Yet Another Uni-Directional State Flow Architecture in Android

Suson Thapa
Aug 2, 2020 · 5 min read
Image for post
Image for post
Photo by June Admiraal on Unsplash

There are many architectures for developing android applications like MVP, MVVM, MVI, etc. By architecture I am referring to presentation layer architecture, you may architect your application using Clean architecture, Hexagonal architecture, Onion architecture, etc. One of the architectures that have proven to be effective is unidirectional state flow which is highly embraced in the web world with React and Redux. In this story, I am going to talk about USF (Unidirectional State Flow) architecture which is inspired by Kaushisk’s talk and his sample app.

This story is not about an introduction to USF. There are many great articles and videos on that topic. I will try to link the references at the end of the story. This story is about my opinion in USF and various problem that I have come across and my solutions to those problems. Hope it helps anyone trying to implement USF.

TL DR; I highly recommend going through the code to understand the concepts.

Recap

Just to recap on USF so that we are on the same page. Here is a visualization of USF.

  1. Events: View produces events which are normally generated from UI elements or from system services.
  2. Results: Events are converted to results generally by ViewModel. Results contains data that is produced from the events.
  3. State: The results are converted to state that needs to be rendered by the view.
  4. Effects: These are one shot events like showing a toast or navigating to other screens.

Kaushik’s (One of the host of Fragmented podcast) implementation of USF is great. I highly recommend listening to episodes 148 and 151 to get better idea on USF.

But there are some issues that I found with his approach. I am not saying his approach is wrong or I am better. It’s just that I want to point out some things that I think in my opinion improves the overall architecture.

View Update

This is one of the major issue with this implementation. This is the ViewState of the app from kaushik’s movies example

This is the function that will render the view state.

One of the issue with this implementation is that the entire view is re-rendered whenever any of the attributes in the ViewState changes. For example, if we change the title then other attributes like adapterList , searchBox etc will be re-rendered.

One of the solution that kaushik pointed out was to use some sort of diffing like DiffUtilCallback for lists and checking for value for string. But in my opinion this solution is not so scalable.

The way that I have tried to solve this issue is by using a wrapper class ViewBox . Here is the implementation of the class.

From now on I will be referencing my own implementation of the moviesUSF. Here is the ViewState for my own implementation.

As you can see I have wrapped all the attributes within the state in ViewBox. Also I have defined two functions, one is stateCopy() that will mark the attributes not in the function parameter to not-changed. The resetCopy() function will mark all the attributes as changed. Don’t worry about ViewVisibility class, I will explain that later.

We can now only render those attributes that changed their values instead of entire state of the view.

This essentially acts as a diffing mechanism.

But how do we know when the attribute has changed. Well the answer is simple, when converting Results to state , we wrap the state attributes in the ViewBox like this.

The stateCopy() takes care of marking other attributes changed state to false.

Initial View State

With kaushik’s implementation there is ScreenLoadEvent which is fired whenever the activity or fragment is resumed. But this implementation implicitly emits the initial state even before the ScreenLoadEvent.

As you can see the last state is replayed whenever a view subscribes to the ViewState. But in my opinion it will be better if we can control the initial emissions. That means we want to emit initial state when the view provides ScreenLoadEvent. This can be achieved with some Rx magic.

We are just skipping the initial state with skip operator, so we are explicitly providing the initial state to the view. This is very important if you want to provide different initial state based on some condition.

We can check weather the fragment is restored from backstack or not and provide different initial state based on that.

Access State in code

There are times when we need to access the current state somewhere in the code. But with current implementation the state is available only within the scan block of resultToState function.

To fix that I defined a new variable mState that stores the state observable and use withLatestFrom operator to obtain the latest value of the state like this.

Then define withLatestFrom extension function to reduce the boiler plate code.

We can then use this function in any observable to get the latest state like this

Animation Issue

To handle the animations and view visibility, I used the ViewVisibility wrapper class to encapsulate both the visibility and animation. I could have also used Effects to trigger the animation but since most of the time visibility and animations are tightly coupled, I just put them in a single class.

LCE State

Kaushik’s implementation returns Results wrapped in LCE. But in my opinion this might create issue when you have more than one part in the screen that shows progress indicators. So I generally use LCE in the repository layer and instead use ContentStatus class to pass the status to the view.

Generic

To reduce boiler plate code I define generic interfaces that the ViewModel and Fragment should implement.

Testing

USF makes testing view model very easy. In my opinion if you properly architecture your code you can even skip Expresso testing. Here is the test class for the sample project.

As you can see we can test almost every aspect of the view without even touching the view. I think this is one of the important benefits of using USF.

That’s it for this story. USF is very vast topic and I recommend you to checkout my sample app and go through the code. I have skipped many implementation details in this post to make it simple. If you have any questions or suggestion feel free to ask in comments.

References

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store