At some point, I noticed periodic conversations about how the
ViewModel actually works from google architectural components. Realizing that I do not fully understand the Internet myself, I was surprised to find that there are an incredible number of similar articles on how to prepare a ViewModel, be friends with LiveData, add dependencies to it through Dagger, link with RxJava and other titles of varying degrees of utility, however, there is almost nothing about what is going on inside.
So I'll try to close the gap myself.
Attention
TL; DR If you feel sorry for the time - shake down to the conclusion, you will lose little.
So the first thing you can pay attention to is that there are 2 different packages of architectural components with ViewModel, namely:
1) Old
android.arch.lifecycle2) New
androidx.lifecycleSpoiler : there is no particular difference between them.
All the work lies behind the challenge:
ViewModelProviders.of(activity).get(MyViewModel::class.java)
Let's start with the method
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 simply checks for null, and
AndroidViewModelFactory is just a thread-safe singleton that stores Application. So they are of no particular interest, the most interesting in the
ViewModelStores.of method:
public static ViewModelStore of(@NonNull FragmentActivity activity) { if (activity instanceof ViewModelStoreOwner) { return ((ViewModelStoreOwner) activity).getViewModelStore(); } return holderFragmentFor(activity).getViewModelStore(); }
At first glance, it looks rather strange - why is it even checking for the presence of the
FragmentActivity ViewModelStoreOwner interface if
it already implements it ? - It wasn’t always like this - until February 2018, when
Support Library 27.1.0 was released , FragmentActivity never implemented ViewModelStoreOwner. At the same time, ViewModel worked quite well for itself.
So let's start with the old case - the
holderFragmentFor method was
launched :
public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); }
Then just get or create a new
holder fragment:
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; }
Well,
HolderFragment itself
is of course
retained public HolderFragment() { setRetainInstance(true); }
Actually, the
ViewModelStere object is stored in it, which in turn holds the
ViewModel pack in itself:
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(); } }
Go back to the case when the support library version is 27.1.0 and higher. FragmentActivity already implements the ViewModelStoreOwner interface, that is, the implementation of the only
getViewModelStore method:
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; } }
Here I will simplify a little -
NonConfigurationInstances is an object with what should not depend on the configuration (obviously from the name), which lies in the
Activity and sweeps inside the
ActivityClientRecord through
ActivityThread during the re-creation between
onStop and
onDestroyIn general, it looks quite funny - instead of a live hacking with ViewModel transfer inside the
retain fragment, the developers made a clever move - they used exactly the same mechanism, but got rid of the need to create an extra fragment each time.
Activity has always had an interesting
onRetainNonConfigurationInstance method. In the Activity class, he essentially did nothing. At all:
public Object onRetainNonConfigurationInstance() { return null; }
Description in the documentation while promising:
If you’re a little bit different, you’ll have to create a new one. You can retrieve your event by yourself.

That is, that there is no sun - it will come out in
getLastNonConfigurationInstance () after recreating the Activity. The developers of architectural components took advantage of this. Of the minuses - up to 4 android does not work, there will have the old fashioned way through the retain fragment.
The ViewModel's
clear () method was called extremely simply - in the
onDestroy FragmentActivity method.
protected void onDestroy() { super.onDestroy(); if (this.mViewModelStore != null && !this.isChangingConfigurations()) { this.mViewModelStore.clear(); } this.mFragments.dispatchDestroy(); }
In fact, with Androidx, almost everything is the same, the only difference is that the getViewModelStore () method is no longer in FragmentActivity, but in
ComponentActivity , from which FragmentActivity is inherited in AndroidX. Only the call to the clear () method has changed, it was taken from onDestroy to an independent callback that is created in the ComponentActivity constructor:
getLifecycle().addObserver(new GenericLifecycleObserver() { @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } });
For the record - during the creation of the article were used:
Support library 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
Findings:
- ViewModel did survive the re-creation of activity in the
retain fragment until the Support library 27.1.0 appeared in February 2018
- C version
Support library 27.1.0 and further, as well as in
AndroidX ViewModel went to wait for the re-creation of the Activity in
FragmentActivity.NonConfigurationInstances (
ComponentActivity.NonConfigurationInstances for AndroidX), in fact by the same mechanism through which retain fragments work, but creating an extra fragment is not required , all ViewModel are sent “near” with retain fragments.
- The mechanism of the ViewModel is almost the
same in AndroidX and the Support library
- If you suddenly need (yes, I can’t even imagine why) you can drag through the data that the Activity should live while the re-creation is
alive - you can use the
onRetainNonConfigurationInstance () /
getLastNonConfigurationInstance () binding- That the old decision, that the new look is something between a documented hack and crutches