What does it take to #BuildBetterApps?

Nikita Barishok
AndroidPub
Published in
6 min readMay 10, 2016

In this story I want to highlight yet another thing to consider in order to build a “better app”. This thing looks straightforward and it’s known by newbie Android developers, but sometimes is used incorrectly even by mature ones.

What are the properties of a “better app”? These properties are at least the next:

  1. Respecting the device. Things should be done with minimal impact on the device hardware/network bandwidth etc.
  2. Respecting the user. This is actually #1 + handling all the possible internal(system behavior)/external(other apps, user) behavior so that UX stays as consistent as possible.

So, with this in mind let’s look how a functionality as simple as fetching data(list of movies, for example) from backend and displaying it to user could be implemented in a “better app”. I’ll refer to the “better app” as “the App” below.

-Bo-o-o-oring! Too easy! It’s matter of ten minutes or so, given we have Retrofit + RxJava set up + UI is static and clear.

-I wish I were as sure as you are.

-??.. Listen, I’ve implemented it hundreds of times, I’ve seen lots of tutorials with code to handle this.. What’s the catch?

-The catch is in the place that every Android developer knows and (likely) hates for its (unnecessary) complexity. This place is as you might have already guessed is activity/fragment lifecycle.

-Oh.. Lifecycle. What about some details?

Some Details

The implementation of the functionality described above basically would look like this (lots lines of code are omitted for clarity):

While this code can work, there are some points where improvement is needed. There are actually several places for it, but I’d like to desribe in this story a particular one..

Saving the data

For code to be compact I’ve put everything in one class, but even if it was implemented using modern MVP approach as it is, everything below would hold true.

So, here onSaveInstanceState() is used to persist data to be restored later. And that’s the wrong path for the App. Here is what you can see in the official documentation about onSaveInstanceState():

This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state.

This is very confusing sentence. That’s because of “before”. The thing is an undetermined amount of time can pass between onPause(), thus onSaveInstanceState(), and onDestroy().

So to make things clearer I’d like to add the fact that objects placed in the bundle should be received synchronously.

Imagine the next situation: user starts using the App, network request fires, and than the nasty phone call comes. While user is talking the result arrives, but it won’t be placed into bundle, because the App is in the background and onSaveInstanceState has already been called. Now the data is in the real danger. If user talks, then googles something, chances are the App could be wiped away from memory. And the received data is lost.

-Why bother? Data can be reloaded after user retruns to the app, so what’s the deal?

-Reloading in this case is a waste. It is a waste for device, therefore for user. Even if you can’t see it properly on your Nexus with WI-FI connected.

If you are building mass-market app, there will be users who are multitasking a lot, who are using the App on slow (really slow) internet, who has device other than Nexus. So such scenarios should be handled properly. No resources should be wasted.

So in short:

Don’t rely on onSaveInstanceState() too much. It should be used only for persisting data that is used only in the corresponding activity/fragment and is received synchronously(user input, current scroll positionon the screen, some flags etc.).

And if you want to persist the view model(data received from backend in our case), find another place for it.

There is approach where it is saved in sqlite database, so that activity/fragment always talks to database and then to the backend. While it works for some types of apps, there some drawbacks with this approach:

  • talking to database is I/O so it is potentially slow to do all the time
  • there are scenarios when you need the data loaded only on one screen, so it is kind of overkill to save it to database in this case
  • there is complexity with managing data because different screens will likely have different types of data

Saving the data like a pro

While this approach is conceptually correct, something tells us, we can do better. And yes, we can.

A better approach is based on the MVP, ViewState, saving presenter’s state during orientation changes along with some other techniques. While it sounds like some terribly overcomplicated thing, in the end of the day this solution appears to be the cleanest and the most reliable one.

So I’d like to give you the implementation scheme for reliable persistance of data for the activity/fragment as I see it. This data is called view state and includes all the things needed to correctly render screen at current moment of time:

  1. view model (list of movies in case of the example)
  2. current activity/fragment state (error, loding etc.)

You can investigate the code right now here. It’s all about building the Apps using MVP + Clean Architecture approach along with some other useful techniques. And here starts the piece about saving activity/fragment state(view state).

In the story I’ll just depict how the aforementioned scenario would be handled for the movie-list-case:

Presentation is separated from business logic here to make scheme more readable (give MVP, Clean Architecture and other approaches you’ll find in this project a try in your code.. You gonna love it!)

This approach mimics onSaveInstanceState(), but doesn’t rely on it at all. It uses some dedicated storage to back up view state. In case of the sample app provided by link the implementation is the next:

  • data(view model) needed by the activity/fragment implements Serializable
  • view state = view model + current_state, implements Serializable
  • view state is serialized and backed up in a plain file in private app storage
  • backup is updated evety time view state changes
  • presenter holds reference to the view state and survives orientation changes of the fragment/activity, so that fragment/activity will likely get in-memory data
  • if app is restoring after being killed because of low memory, the view state will be loaded from file in same fashion as if it was pulled from the Bundle, but in this case there won’t be data loss
  • when the screen is no longer needed, the file with view state can be safely deleted.

And you see smile on your users’ faces.

Conclusion

What I tried to highlight is the fact that you shouldn’t hide behind the false sense of reliability that method name “onSaveInstanceState” gives. It actually can be applied in the narrow set of cases. To provide reliable means of persisting asynchronously received data (even temporary, which lifecycle equals to activity/fragment lifecycle) some technique other than onSaveInstanceState() should be used. And the approach provided above is an example.

While the suggested scheme and implementation may look complex, it is the fair price for the next benefits:

While some things seem obvious and clear, like onSaveInstanceState(), they can hide some nasty limitations. So that even the small functionality appears to take fair amount of time to be built properly. And it has to be done, given you are going to #BuildBetterApps.

Think critically while crafting your applications. Strive for better.

--

--