I have a tablet that defaults in landscape mode. Occasionally, I install an app from the Play Store only to find out that it’s locked in portrait mode, meaning that I need to rotate my device in order to use it. Of course, I immediately uninstall it. 🙂

Many popular apps are a victim of this design. Facebook Lite is one, which is used by millions of people. Also, in the Facebook app some screens (i.e. Activities) are also locked in portrait mode.

This is not only about tablets though. The orientation is locked for regular phones as well, so a user cannot rotate their phone to view a particular screen in landscape mode, if they want.

Are developers lazy?

Why do developers choose to lock the orientation? Are they just lazy? Don’t they want to please their users and even target more?

Well, that might be one reason. The most important one, in my opinion, is that rotation handling is difficult. If something is easy, people will use it. If not, they will just ignore it.

Why rotation handling is difficult

When the orientation changes, activities are re-created, so that the views can be created with the correct dimensions and possibly other things. Because the activity is recreated, the following problems arise:

Variables stored in the activity are reset to their default values on orientation change

If a background task (e.g. an AsyncTask) that was created within the activity finishes, it has nowhere to pass its results to, as the activity is now a new instance. Also, this can cause memory leaks as the reference to the old instance of the activity is still around (implicitly kept by the AsyncTask), so it cannot be GC’ed (garbage-collected) until the task finishes.

For AsyncTasks, the proposed solution is to create a Fragment with setRetainInstance(true) , which I don’t really like, as it means that the fragment now needs to communicate with the activity and more importantly, I don’t like having AsyncTasks in activities and fragments as it tends to make them god classes.

Separate concerns with MVVM

With the introduction of ViewModel in Android Jetpack, handling orientation changes has become a lot easier.

We will use the view model as part of the Model View ViewModel pattern. This allows us to separate the concerns of our app:

Component Description Example View Responsible for displaying data and interacting with the user Activities, Fragments, other custom views Model Provides data

Encapsulates business logic Making a network call, loading items from a database ViewModel Glue between the view and the model

Why MVVM?

I chose MVVM because ViewModel instances of Android Jetpack remain alive even when the orientation changes!

This helps a lot as we now have a place to keep view-related data.

In the past I have been using MVP (Model – View – Presenter) but I had to implement logic to keep presenters alive in a global cache, take care to pass missed events because of orientation change to the view (activity, fragment) and more. When ViewModel was released, it was a no brainer for me to use it.

A ViewModel provides a handy place to keep data that should be persisted when the orientation changes.

public class TimerViewModel extends ViewModel { private MutableLiveData elapsedTimeLiveData = new MutableLiveData<>(); /** * @return elapsed time in seconds */ public LiveData getElapsedTime() { return elapsedTimeLiveData; } }

What about onSaveInstanceState() ?

The android framework already provides a way to save state when the orientation changes. Why did we choose to keep data in the ViewModel instead? Well, here are some reasons:

The amount of data that can be saved in the bundle when the orientation changes is limited. It just won’t work for large collection of items, e.g. items in a RecyclerView (an exception will be thrown)

It’s easier to make async requests, for example load some data, from the viewmodel, because as mentioned before, it remains alive even when the orientation changes

Is onSaveInstanceState() useless then? No! It’s very useful and you will need to use it together with a view model in many occasions. Use it to save parameters that were passed to the activity / fragment, e.g. the ID of the user in a UserProfile activity. This is important because data saved in the bundle survive not only orientation changes, but also process restarts whereas the ViewModel does not.

Our example

To demonstrate a usage of ViewModel, we are going to create a simple timer app, with the following functionality:

Display elapsed time

Timer continues to run when the screen is rotated

Start the timer

Pause the timer

Reset the timer If already started, restarts If paused, stays at 0



We are going to create the following classes:

class Responsibilities TimerActivity (View) Displays elapsed time

Accepts user input (start, pause, reset) Timer (Model) Contains the logic of starting, pausing and resetting

Provides a way for other classes to access the elapsed time (using a listener)

Periodically calls its listener to let it know of the new elapsed time TimerViewModel (ViewModel) Creates a Timer

Accepts user input (start, pause, reset) and forwards the command to the timer

Notifies the view with the latest elapsed time

TimerActivity

Here we need to create the view model, forward user actions to and observe the elapsed time.

Create the ViewModel

We define the ViewModel as a class variable and initialize it in onCreate() . To initialize it, we call ViewModelProviders.of() . This is important, because it ensures that even when the activity is recreated, we will be given the same ViewModel instance.

public class TimerActivity extends AppCompatActivity { private TimerViewModel viewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_timer); // ... viewModel = ViewModelProviders.of(this, new TimerViewModel.Factory()).get(TimerViewModel.class); // ... } }

Forward actions to the ViewModel

Simply add listeners to our buttons and call the ViewModel:

startButton = findViewById(R.id.start); startButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewModel.start(); } });

Observe elapsed time

ViewModel returns a LiveData object which we can observe for changes.

viewModel.getElapsedTime().observe(this, new Observer() { @Override public void onChanged(Double elapsedTime) { elapsedTimeView.setText(String.format(Locale.getDefault(), "%.1f", elapsedTime)); } });

TimerViewModel

The ViewModel creates the timer and listens to elapsed time updates.

When the elapsed time changes, a LiveData object is modified, providing a way for the view to observe the updated value.

public class TimerViewModel extends ViewModel implements Timer.Listener { private final Timer timer = new Timer(); private TimerViewModel() { timer.setListener(this); } @Override public void onTimeUpdated(double elapsedTime) { // Using postValue() since it's called from a background thread. elapsedTimeLiveData.postValue(elapsedTime); } // ... }

It also forwards user actions to it:

public void reset() { timer.reset(); }

There’s a bit more to it, so make sure you check the full code.

Timer

The timer class provides the functionality to start, pause, stop and listen to the elapsed time. Since this is not the main topic of this post, I’m omitting its code.

See the code on Github

The full code is available on GitHub.

It also contains clean-up code (stopping the timer) and a way for the view to observe whether the timer is started or not, so that it can show the start or the pause button respectively.

Conclusion

The ViewModel class makes handling orientation changes a lot easier than before.

If you are a developer who has locked the orientation to portrait because it was difficult to implement rotation handling, now is the time to reconsider. 😎