Android ViewModel recreated on screen rotation

tomwyr picture tomwyr · Apr 24, 2018 · Viewed 12k times · Source

I found a case when architecture components ViewModel isn't retained - in short it goes as follows:

  1. Activity is started and ViewModel instance is created
  2. Activity is put to background
  3. Device screen is rotated
  4. Activity is put back to foreground
  5. ViewModel's onCleared method is called and new object is created

Is it normal behavior of Android that my ViewModel instance is getting destroyed in this case? If so, is there any recommended solution of keeping its state?

One way I can think of is saving it once onCleared is called, however, it would also persist the state whenever activity is actually finishing. Another way could be making use of onRestoreInstanceState but it's fired on every screen rotation (not only if the app is in background).

Any silver bullet to handle such case?

Answer

Surendar Dharavath picture Surendar Dharavath · Jul 23, 2018

Yes @tomwyr, this was a bug from an android framework. Bug details

The fix is available in 28.0.0-alpha3 and AndroidX 1.0.0-alpha3

But if you don't want to update to above versions now itself, Then you can solve like this (I know this is a bad solution but I didn't see any other good way)

In your activity override onDestroy method and save all the required fields to local variables before calling super.onDestroy. Now call super.onDestroy then Initialize your ViewModel again and assign the required fields back to your new instance of ViewModel

about isFinishing

Below code is in Kotlin:

override fun onDestroy() {
     val oldViewModel = obtainViewModel()

     if (!isFinishing) { //isFinishing will be false in case of orientation change

          val requiredFieldValue = oldViewModel.getRequiredFieldValue()

          super.onDestroy

         val newViewModel = obtainViewModel()

         if (newViewModel != oldViewModel) { //View Model has been destroyed
              newViewModel.setRequiredFieldValue(requiredFieldValue)
          }
      } else {
         super.onDestroy
      }
 }

private fun obtainViewModel(): SampleViewModel {
      return ViewModelProviders.of(this).get(SampleViewModel::class.java)
}