Persisting Android UI State
Learn how to fix Android UI state loss on rotation bugs and how to use Android’s saved instance state mechanism to fix UI state loss on process death.
Save 35% off the list price* of the related book or multi-format eBook (EPUB + MOBI + PDF) with discount code ARTICLE.
* See informit.com/terms
Android does a great job of providing alternative resources at the right time. However, destroying and re-creating activities on rotation can cause headaches, such as GeoQuiz’s bug of reverting back to the first question when the device is rotated.
To fix this bug, the post-rotation MainActivity instance needs to know the old value of currentIndex. You need a way to save this data across a runtime configuration change, like rotation.
In this chapter you will fix GeoQuiz’s UI state loss on rotation bug by storing its UI data in a ViewModel. You will also address a less easily discoverable, but equally problematic, bug – UI state loss on process death – using Android’s saved instance state mechanism.
Including the ViewModel Dependency
In a moment, you are going to add a ViewModel class to your project. The ViewModel class comes from an Android Jetpack library called lifecycle-extensions, one of many libraries that you will use throughout this book. (You will learn more about Jetpack later in this chapter.) To use the class, you must first include the library in the list of dependencies for your project.
Your project’s dependencies live in a file called build.gradle (recall that Gradle is the Android build tool). In the project tool window, with the Android view enabled, expand the Gradle scripts section and take a look at the contents. Your project actually comes with two build.gradle files: one for the project as a whole and one for your app module. Open the build.gradle file located in your app module. You should see something similar to Listing 4.1.
Listing 4.1 Gradle Dependencies (app/build.gradle)
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { ... } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.0.0-beta01' ... }
The first line in the dependencies section specifies that the project depends on all of the .jar files in its libs directory. The other lines list libraries that were automatically included in your project based on the settings you selected when you created it.
Gradle also allows for the specification of new dependencies. When your app is compiled, Gradle will find, download, and include the dependencies for you. All you have to do is specify an exact string incantation and Gradle will do the rest.
Add the lifecycle-extensions dependency to your app/build.gradle file, as shown in Listing 4.2. Its exact placement in the dependencies section does not matter, but to keep things tidy it is good to put new dependencies under the last existing implementation dependency.
Listing 4.2 Adding lifecycle-extensions Dependency (app/build.gradle)
dependencies { ... implementation 'androidx.constraintlayout:constraintlayout:1.1.2' implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' ... }
When you make any change to build.gradle, Android Studio will prompt you to sync the file (Figure 4.1).
FIGURE 4.1 Gradle sync prompt
This sync asks Gradle to update the build based on your changes by either downloading or removing dependencies. To sync, either click Sync Now in the prompt or select File → Sync Project with Gradle Files.