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.
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
Just to recap on USF so that we are on the same page. Here is a visualization of USF.
- Events: View produces events which are normally generated from UI elements or from system services.
- Results: Events are converted to results generally by ViewModel. Results contains data that is produced from the events.
- State: The results are converted to state that needs to be rendered by the view.
- Effects: These are one shot events like showing a toast or navigating to other screens.
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.
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
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
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
state , we wrap the state attributes in the
ViewBox like this.
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
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
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.
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
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.
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.
To reduce boiler plate code I define generic interfaces that the ViewModel and Fragment should implement.
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.