This is the 14th day of my participation in the August Text Challenge.More challenges in August

This chapter focuses on using ViewModel to save UI data and fixing UI state loss defects in the GeoQuiz application.

First, introduce ViewModel dependencies

The ViewModel class is designed to store and manage interface-related data in a life-cycle oriented manner. The ViewModel class allows data to persist after configuration changes such as screen rotation.

It comes from the Android Jetpack library for Lifecycle -Extensions, currently deprecated apis in Lifecycle -Extensions. You can add the required dependencies for a particular Lifecycle artifact. Reference:

Developer.android.com/jetpack/and…

Implementation 'androidx. Lifecycle: lifecycle - viewmodel - KTX: 2.4.0 - alpha03Copy the code

Then click Sync Now.

Add ViewModel

  • Create QuizViewModel

    private const val TAG = "QuizViewModel" class QuizViewModel : ViewModel() {init {log.d (TAG, "ViewModel instance created")} /** * On cleared * onCleared() is called just before the ViewModel is destroyed. Good for some cleanup, such as untying a data source. */ override fun onCleared() { super.onCleared() Log.d(TAG, "ViewModel instance about to destroyed") } }Copy the code
  • Access to the ViewModel

    The method of accessing the ViewModel in the book has been deprecated and, as mentioned earlier, my practice did not introduce the Lifecycle extensions, so the actual code changed slightly.

    Add to the onCeate() method of mainActivity.class:

    override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)
          ...
          val quizViewModel by lazy { ViewModelProvider(this).get(QuizViewModel::class.java) }
    
          ...
    }
    Copy the code

    I just changed the API to get an instance of quizViewModel.

    When MainActivity first accesses QuizViewModel, the ViewModelProvider creates and returns a new instance of QuizViewModel. When MainActivity accesses the QuizViewModel object again after the device configuration changes, it returns the Previously created QuizViewModel. When MainActivity completes its mission destruction (for example, when the user presses the back key), the viewModel-activity pair is erased from memory.

2.1 ViewModel Lifecycle and ViewModelProvider

The code above means that a ViewModel instance is associated with an activity lifecycle, and regardless of the state of the associated activity, the ViewModel remains in memory until the associated activity is destroyed.

When the device rotates, the ViewModel is also left in memory.

Run the GeoQuiz application log:

Rotate device log :(you can see that the viewmodel is not rebuilt, but is directly fetched from memory for the first time)

Exit the application log :(viewmodel is destroyed)

Summary: The relationship between QuizViewModel and MainActivity is one-way. An activity references its associated ViewModel, not the other way around. A ViewModel should never reference an activity or view, or it will cause a memory leak.

Memory leaks occur when an object strongly references another object to be destroyed. Such a strong reference prevents the garbage collector from cleaning objects from memory. Memory leaks from device configuration changes are a common problem.

2.2 Adding data to the ViewModel

The ViewModel holds the data needed to associate the user interface and formats it for easy access by other objects. This allows you to “slim down” the activity by removing the screen presentation logic from it.

class QuizViewModel : ViewModel() {

    var currentIndex = 0

    private val questionBank = listOf(
        Question(R.string.question_australia, true),
        Question(R.string.question_oceans, true),
        Question(R.string.question_mideast, false),
        Question(R.string.question_africa, false),
        Question(R.string.question_americas, true),
        Question(R.string.question_asia, true)
    )

    val currentQuestionAnswer: Boolean get() = questionBank[currentIndex].answer

    val currentQuestionText: Int get() = questionBank[currentIndex].textResId

    fun moveToNext() {
        currentIndex = (currentIndex + 1) % questionBank.size
    }
}
Copy the code

Using the by lazy keyword ensures that the quizViewModel property is of type val, not var. Get and save QuizViewModel only after the Activity instance object is created; that is, QuizViewModel is assigned one value at a time.

Save data during process destruction

In the event of a configuration change such as screen rotation, the activity is destroyed and restarted, and the ViewModel can be used to automatically save and retrieve data. However, if the entire Android system is running out of memory and the app is not in the foreground, the system may simply wipe out the entire app process. In this case, the ViewModel will not work, because it is also gone.

3.1 Override onSaveInstanceState(Bundle) function

By overriding the Activity. OnSaveInstanceState (Bundle), you can solve the above problems, when the application process in the system by accident “kill”, help the user to save some key data is not very big, so at the time of loading the app again restore state.

3.2 Preserve instance state and activity records

Add a stashed state to the activity lifecycle:

Note that you don’t need to call onDestroy() for an activity to enter the staging state. However, onStop() and onSaveInstanceState(Bundle) are two reliable functions (unless there is a major device failure).

In general, the onSaveInstanceState(Bundle) function is overridden. In the Bundle object, data about the current activity’s small or temporary state is stored. Overrides the onStop() function to hold permanent data, such as user-edited text, etc.

To test that the system is running out of memory to kill the app, go to the Developer option and leave the activity on. Then after the app starts, click the home button and the system will automatically kill the app. As shown below:

ViewModel and save instance state

Neither preserving instance state nor ViewModel is a long-term storage solution. If your application needs to store data for a long time and you don’t have to worry about activity state at all, consider using a persistent storage solution. (We’ll learn later)

The ViewModel will always operate on in-memory data, so it will be faster, plus the example of the GeoQuiz application in the book, the questions are hard coded, not fetched from the network, and the data is not much, and does not need to be stored in a database, so it is reasonable to use the ViewModel solution for this application.

Therefore, to handle both device configuration changes and system-initiated process terminations, a combination of ViewModel and onSaveInstanceState() is used to store data state.

5. In-depth study: Jetpack, AndroidX and architectural components

The Jetpack library falls into four broad categories: Foundation, Architecture, Behavior, and UI.

Another common name for the Architecture class Jetpack library is Architecture Component. The ViewModel is an architectural component.

Reference: developer.android.com/jetpack

Six, in-depth study: solve problems thoroughly

Means by banning application screen rotation, so as to solve the equipment configuration change of UI state lost the way too rough, cannot fundamentally solve the problem, it also should process destroyed problem not solve, in the process of development, will also face other problems related to the life cycle, we have to check the root, and then learn some technical points of knowledge, to solve the problem of development!

Seven, finally

The practice code address of this article:

Github.com/visiongem/A…

Because will according to their own meaning practice exercises, so with the book sample code is a little different, for reference only. Make your own improvements if you’re interested. Real knowledge comes from practice! Fill yourself up! 🆙


🌈 follow me ac~ ❤️

Public account: Ni K Ni K