À un moment donné, j'ai remarqué des discussions périodiques sur le fonctionnement
réel de ViewModel à partir des composants architecturaux de Google. Réalisant que je ne comprenais pas moi-même, j'ai grimpé sur Internet et j'ai été surpris de constater qu'il y avait une quantité incroyable d'articles identiques sur la façon de cuisiner ViewModel, de se faire des amis avec LiveData, de glisser des dépendances via Dagger, de copuler avec RxJava et d'autres titres de divers degrés d'utilité, cependant, il n'y a presque rien sur ce qui se passe à l'intérieur.
Je vais donc essayer de combler le vide moi-même.
Attention
TL; DR si vous vous sentez désolé pour le temps - descendez à la sortie, vous perdrez peu.
Donc, la première chose à laquelle vous pouvez faire attention est qu'il existe 2 packages différents de composants architecturaux avec ViewModel, à savoir:
1) Ancien
android.arch.lifecycle2) Nouveau
androidx.lifecycleSpoiler : Il n'y a pas beaucoup de différence entre eux.
Tout le travail se cache derrière le défi:
ViewModelProviders.of(activity).get(MyViewModel::class.java)
Commençons par la méthode of
public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); } public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(ViewModelStores.of(activity), factory); }
checkApplication vérifie simplement null, et
AndroidViewModelFactory n'est qu'un singleton thread-safe qui contient Application. Ils ne présentent donc pas d'intérêt particulier, le plus intéressant étant la méthode
ViewModelStores.of :
public static ViewModelStore of(@NonNull FragmentActivity activity) { if (activity instanceof ViewModelStoreOwner) { return ((ViewModelStoreOwner) activity).getViewModelStore(); } return holderFragmentFor(activity).getViewModelStore(); }
À première vue, cela semble plutôt étrange - pourquoi une
FragmentActivity devrait-elle vérifier la présence de l'interface
ViewModelStoreOwner si
elle l'implémente déjà ? - Ce n'était pas toujours le cas - jusqu'à la fin de février 2018, lorsque la version
27.1.0 de la bibliothèque de support a été publiée , FragmentActivity n'avait jamais implémenté ViewModelStoreOwner. En même temps, ViewModel a fonctionné pour lui-même.
Commençons donc par l'ancien cas - la méthode
holderFragmentFor a été
lancée :
public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); }
Ensuite, obtenez ou créez un nouveau fragment de
support :
HolderFragment holderFragmentFor(FragmentActivity activity) { FragmentManager fm = activity.getSupportFragmentManager(); HolderFragment holder = findHolderFragment(fm); if (holder != null) { return holder; } holder = mNotCommittedActivityHolders.get(activity); if (holder != null) { return holder; } if (!mActivityCallbacksIsAdded) { mActivityCallbacksIsAdded = true; activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks); } holder = createHolderFragment(fm); mNotCommittedActivityHolders.put(activity, holder); return holder; }
Eh bien,
HolderFragment lui
- même
, bien sûr,
conservé public HolderFragment() { setRetainInstance(true); }
En fait, l'objet
ViewModelStore y est stocké, qui à son tour contient un ensemble de
ViewModel :
public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); if (oldViewModel != null) { oldViewModel.onCleared(); } } final ViewModel get(String key) { return mMap.get(key); } public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); } }
Revenons au cas où la version de la bibliothèque de support est 27.1.0 et supérieure. FragmentActivity implémente déjà l'interface ViewModelStoreOwner, c'est-à-dire qu'il implémente la seule méthode
getViewModelStore :
public ViewModelStore getViewModelStore() { if (this.getApplication() == null) { throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call."); } else { if (this.mViewModelStore == null) { FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance(); if (nc != null) { this.mViewModelStore = nc.viewModelStore; } if (this.mViewModelStore == null) { this.mViewModelStore = new ViewModelStore(); } } return this.mViewModelStore; } }
Ici, je vais simplifier un peu -
NonConfigurationInstances est un objet qui ne devrait pas dépendre de la configuration (évidemment du nom), qui se trouve dans l'
activité et balaie l'intérieur de
ActivityClientRecord à
ActivityThread pendant la récréation entre
onStop et
onDestroyEn général, cela semble assez drôle - au lieu d'un piratage de la vie avec un transfert de ViewModel à l'intérieur du fragment de
conservation , les développeurs ont fait un geste délicat - ils ont utilisé exactement le même mécanisme, mais se sont débarrassés de la nécessité de créer un fragment supplémentaire à chaque fois.
L'activité a toujours eu une méthode
onRetainNonConfigurationInstance intéressante. Dans la classe d'activité, il n'a pratiquement rien fait. En général:
public Object onRetainNonConfigurationInstance() { return null; }
La description dans la documentation est prometteuse:
Appelé par le système, dans le cadre de la destruction d'une activité en raison d'un changement de configuration, lorsqu'il est connu qu'une nouvelle instance sera immédiatement créée pour la nouvelle configuration. Vous pouvez renvoyer n'importe quel objet que vous aimez ici, y compris l'instance d'activité elle-même, qui peut ensuite être récupérée en appelant getLastNonConfigurationInstance () dans la nouvelle instance d'activité.

C'est-à-dire ce qu'il ne faut pas y mettre - il sera
analysé dans
getLastNonConfigurationInstance () après avoir recréé l'activité. Les développeurs de composants architecturaux en ont profité. Parmi les inconvénients - jusqu'à 4 androïdes ne fonctionnent pas, il faudra le faire à l'ancienne en conservant le fragment.
La méthode
clear () de ViewModel a été appelée très simplement - dans la méthode
onDestroy FragmentActivity.
protected void onDestroy() { super.onDestroy(); if (this.mViewModelStore != null && !this.isChangingConfigurations()) { this.mViewModelStore.clear(); } this.mFragments.dispatchDestroy(); }
En fait, avec Androidx presque tout est identique, la seule différence est que la méthode getViewModelStore () n'est plus dans FragmentActivity, mais dans
ComponentActivity , dont FragmentActivity est hérité dans AndroidX. Seul l'appel à la méthode clear () a changé; il a été supprimé de onDestroy à un rappel indépendant, qui est créé dans le constructeur ComponentActivity:
getLifecycle().addObserver(new GenericLifecycleObserver() { @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } });
Pour le protocole - lors de la création de l'article, les éléments suivants ont été utilisés:
Bibliothèque de support 27.0.0, 28.0.0
androidx.lifecycle: lifecycle-viewmodel: 2.0.0
androidx.lifecycle: lifecycle-extensions: 2.0.0
android.arch.lifecycle: extensions: 1.1.1
android.arch.lifecycle: viewmodel: 1.1.1
Conclusions:
- ViewModel a vraiment survécu à la recréation d'activité dans le fragment de
conservation jusqu'à la bibliothèque de support 27.1.0 qui est apparue en février 2018
- À partir de la
bibliothèque de support version
27.1.0 , ainsi que dans
AndroidX ViewModel, je suis allé attendre pour recréer l'activité dans
FragmentActivity.NonConfigurationInstances (
ComponentActivity.NonConfigurationInstances for AndroidX), en fait le même mécanisme à travers lequel conserver les fragments fonctionne, mais la création d'un fragment supplémentaire n'est pas nécessaire , tous les ViewModel sont envoyés "à côté de" avec des fragments de conservation.
- Le mécanisme de ViewModel est presque le
même dans AndroidX et la bibliothèque de support
- Si vous devez soudainement (oui, je ne peux pas imaginer pourquoi) faire glisser des données qui devraient vivre pendant la durée de l'activité, mais en même temps
prendre en compte la récréation - vous pouvez utiliser le
onRetainNonConfigurationInstance () /
getLastNonConfigurationInstance ()- Quelle est l'ancienne décision, à quoi ressemble le nouveau quelque chose entre un hack documenté et des béquilles