web-dev-qa-db-fra.com

Android ViewModel recréé en rotation d'écran

J'ai trouvé un cas où les composants d'architecture ViewModel ne sont pas conservés - bref, cela se passe comme suit:

  1. L'activité est démarrée et l'instance ViewModel est créée
  2. L'activité est mise en arrière-plan
  3. L'écran de l'appareil est pivoté
  4. L'activité est remise au premier plan
  5. La méthode onCleared de ViewModel est appelée et un nouvel objet est créé.

Est-ce un comportement normal d'Android que mon instance ViewModel soit détruite dans ce cas? Si tel est le cas, existe-t-il une solution recommandée pour conserver son état? 
Une façon dont je peux penser est de le sauvegarder une fois que onCleared est appelé. Cependant, l'état persisterait également chaque fois que l'activité s'achèverait. Une autre méthode pourrait être d'utiliser onRestoreInstanceState mais elle est déclenchée à chaque rotation d'écran (pas seulement si l'application est en arrière-plan). 
Une balle en argent pour gérer un tel cas?

4
tomwyr

Oui @tomwyr, c’était un bug d’un framework Android. Détails du bug

Le correctif est disponible dans 28.0.0-alpha3 et AndroidX 1.0.0-alpha3

Mais si vous ne voulez pas mettre à jour les versions ci-dessus, alors vous pouvez le résoudre comme ceci ( Je sais que c'est une mauvaise solution, mais je n'ai pas vu d'autre bon moyen )

Dans votre activité, remplacez la méthode onDestroy et enregistrez tous les champs obligatoires dans les variables locales avant d'appeler super.onDestroy . Maintenant, appelez super.onDestroy puis initialisez à nouveau votre ViewModel et réattribuez les champs requis à votre nouvelle instance de ViewModel.

à propos isFinishing

Le code ci-dessous est en 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)
}
6
Surendar Dharavath

Autant que je sache, le seul but de ViewModel est de survivre et de conserver les données (c'est-à-dire "save the state") pendant que son propriétaire passe par différents événements du cycle de vie. Donc, vous n'avez pas à "sauver l'Etat" vous-même.

Nous pouvons en déduire que ce n'est "pas un comportement normal". onCleared() n'est appelé qu'une fois l'activité terminée (et ne sera pas recréé).

Créez-vous la ViewModel à l'aide de la ViewModelProvider ou créez-vous l'instance à l'aide du constructeur?

Dans votre activité, vous devriez avoir quelque chose comme:

// in onCreate() - for example - of your activity
model = ViewModelProviders.of(this).get(MyViewModel.class);
// then use it anywhere in the activity like so
model.someAsyncMethod().observe(this, arg -> {
    // do sth...
});

En faisant cela, vous devriez obtenir l'effet attendu.

0
Ace

Changez la bibliothèque de support/compileSDK/targetSDK en 28.

J'ai eu un problème similaire avec multi-fenêtre. Lors du passage en écran partagé, mon viewModel est recréé. Bibliothèque de support 28 corrigé mon problème. (Ma version du cycle de vie est 1.1.1)

0
Feng Shi