Marcos Placona
Marcos Placona
Developer evangelist at Twilio, GDE and in ❤️ with Open Source
3 min read

Categories

  • Android


Android Jetpack and Lifecycles

Handling configuration changes in Android can be a tricky subject. Configuration changes happen when you close an application, but also when you change the device’s orientation for example.

Historically it’s been common practice to use onSaveInstanceState so your data survived configuration changes. The drawback to this is that using this doesn’t allow for storing much data about the view.

The problem

Let’s look at an example app that we’ve built that generates a random number between 1 and 42 when the application starts.

Running that application should give us a result like this:

Sample application running in portrait shows a random number

Great, we’ve been able to do this by writing the following code and calling that method from our activity:

private fun createMagicNumber(){
    myRandomNumber = (1..42).shuffled().first()
}

Turning the device sideways should also give us that same result… right? Not really because that’s a configuration change, so any data we have on the screen gets fetched again. Were this an expensive API request, for example, we’d be making it again.

Sample application running in landscape shows a different random number

While this is just a simple application and the data here could very easily be stored inside onSaveInstanceState, if we have a lot of data showing on the screen, this solution would be less than robust and limited to the amount of data we can save.

The solution

When an activity is created or recreated, it handles a few different tasks such as

  • displaying the UI
  • keeping track of user interaction
  • handling the communication with the OS

And obviously loading any data from the network or database. Doing this time and time again can be expensive and if you have a lot of data, it means the user needs to get that data from somewhere every-single-time.

Using ViewModels we can store and manage all of our UI-related data in a lifecycle conscious way.

Here are the changes I had to make to the number generator project in order to benefit from that.

I first started by adding a dependency to the lifecycle extensions into my project.

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'

+ def lifecycle_version = "1.1.1"
+ implementation "android.arch.lifecycle:extensions:$lifecycle_version"

testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

I then modified my Datasource class to extend [ViewModel](https://developer.android.com/reference/androidx/lifecycle/ViewModel.html). This class generates a number between 1 and 42, but making it extend ViewModel as I did, changes its behaviour so it keeps state between configuration changes.

package uk.co.placona.jetpackdemo1

- class DataSource {
+ import android.arch.lifecycle.ViewModel
+ 
+ class DataSource: ViewModel() {
    private val tag = MainActivity::class.java.simpleName
    private lateinit var myRandomNumber:Number

Lastly, I changed the MainActivity so instead of calling the getMagicNumber() method directly, I now call it through the View Model Provider as follows:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

-    val myData = DataSource()
-    tvMagicNumber.text = myData.getMagicNumber().toString()
+    val model = ViewModelProviders.of(this).get(DataSource::class.java)
+    tvMagicNumber.text = model.getMagicNumber().toString()
}

With that, we have the ViewModel now processing the data that needs to be sent to the UI as well as performing any logic. As an added bonus it also survives any configuration changes.

A gif showing how the application behaves upon rotation after changes