Featured Articles
Article in Android App Development category.
Why MVI? —The State Problem
To understand what the State problem is, let’s first have a look at what is State and how we can model it for a particular…
This is the second article in Why MVI series. You might be interested to read about the motivation, for that you can have a look at the introductory article here. To understand what the State problem is, let’s first have a look at what is State and how we can model it for a particular screen.
State
Any variable which is used to depict what a view component is currently doing is its state. View is currently refreshing
. Login Button enabled
. Current Page Number
. List
of Items. Error Message
. Is User Logged In
. etc. Altogether, these constitute the state of any screen.
In MVI, State is the single source of truth. Instead of View, Presenter and Data Layer having their own states we have just one State object which represents the state of the whole screen.
Let’s try to model a State object for a screen.
ItemsFragment
We have a screen called ItemsFragment
as shown in the diagram. We will try to model a State
object for this screen. For simplicity we are not considering the TabLayout
or BottomNavigationView
and instead going to just focus on the highlighted part.
Loading
The progress indicator which indicates that the items are currently loading can be represented as a Boolean
: loading
.Currentlythe List
of items is empty.
EmptyStateVisible
After getting the results from our Repository
, we could get an empty result which will show an empty state on the screen. It can be represented as a Boolean
: emptyStateVisible
.
Refreshing
User can also pull to refresh the screen which can be represented as a Boolean
: refreshing
. Do note that loading
and refreshing
are two separate states as they represent state of different view elements.
No Internet
Next we have an error state of the view which can be represented as a String
:errorMsg
on the screen. It is currently set to “No Internet”.
Items List
In case our view shows some items, our list is going to be non-empty and state will contain List<Item>
.
State Problem
Let’s now have a glimpse of a simple state problem in an app below.
We are loading some items and we thus see a loading indicator. Then, we get an empty result from the server and we show an empty state to the user which says “No Items”. The user then hits Pull to Refresh and even though we get an empty response from the server again. The pull to refresh indicator keeps showing forever.
This is a simple example of a state problem from one of my own apps. These kinds of issues can creep into apps from seemingly nowhere. I have seen many good apps with issues such as Error State and Loading indicators shown at the same time.
Misusing MVP
Let’s figure out how we caused the above issue by misusing our favorite pattern.
This Diagram shows various elements in MVP + User. Time is represented on the y-axis
and increases from top to bottom.
The user first opens the app (start()
) and View starts loading data. View calls showLoading()
on itself which shows a ProgressBar on the screen. View changes its own state without Presenter knowing anything about it. This is one of the ways in which we can misuse MVP.
View then calls loadItems()
on the Presenter. Presenter gets data from the Repository
and we get an empty result. Presenter calls showEmptyState()
and hideLoading()
on View.
Presenter tries to drive the state of the view and some of the state is kept in Presenter while some is kept in the View.
The user hits pull to refresh on the screen (refresh()
). View calls setRefreshing()
on itself. Again View
changes it’s own state without Presenter knowing anything about it.
View then calls refresh()
on the Presenter. Presenter gets data from the Repository
and we get an empty result. Presenter calls showEmptyState()
and hideLoading()
on view.
Presenter knows nothing about the refreshing
state of view.
Presenter only knows about the MVP contract which exposes a method called hideLoading()
and that’s what Presenter calls.
There’s no loader to hide and thus the Pull to Refresh indicator keeps on showing forever and we run into a State Problem.
What went wrong?
- Presenter, View and even Business Logic: everything has its own State.
- Presenter coordinates View State.
- Presenter has multiple outputs and as the complexity of screen increases, it becomes hard to manage these output sites from Presenter.
Solution
If you remember, we created a single state object depicting the above problem. If there’s a single source of truth between various layers, there is only a single place where it can be modified. If there is only a defined set of actions that can be performed and for each action, there’s a defined way to change the state of our system. We can then solve this problem. This is what MVI suggests.
In the next part of the series, we will look at how we can extend our existing patterns and apply the guidelines from the MVI world to solve the problem above without having to start from scratch. A sample repository can be found here.