When is the viewmodel onCleared called
AndroidAndroid ActivityAndroid LifecycleAndroid ViewmodelAndroid Problem Overview
Are ViewModels independent of activity/fragment lifecycles or just their configuration changes. When will they cease to exist and the subsequent onCleared() method called. Can the viewModel be shared with another Activity ?
A situation:
Activity1+viewModel1--->(rotation)--->Activity1+viewModel1
--->(launch Intent)--->Activity2+viewModel1
is this sharing possible and is it a good practice.
Also, since the app lifecycle callbacks, onPause->onStop->onDestroy is same for both
1.activity rotating and
2.when an Activity ends,
how is a ViewModel figuring out internally the right time to call onCleared and finally end its lifecycle.
Findings:
the ViewModel uses a holderFragment internally to hold an instance of the activity and uses the setRetainInstance method like fragments to account for configuration changes.
Source: dive-inside-of-androids-viewmodel-architecture-components
Android Solutions
Solution 1 - Android
> Are ViewModels independent of activity/fragment lifecycles or just > their configuration changes.
ViewModels (VMs) are independent of configuration changes and are cleared when activity/fragment is destroyed.
Following is the lifecycle of ViewModel from official site:
> Can the viewModel be shared with another Activity ?
You shouldn't do that with Activities. However fragments can share a ViewModel
using their activity scope to handle communication between them
> How is a ViewModel figuring out internally the right time to call onCleared
and finally end its lifecycle?
A VM's onCleared
is called when the app is put into the background and the app process is killed in order to free up the system's memory.
See the Do ViewModels persist my data? section from this Android Developer's post, ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders
If you want the user to be able to put the app into the background and then come back three hours later to the exact same state, you should also persist data. This is because as soon as your activity goes into the background, your app process can be stopped if the device is running low on memory.
If the app process and activity are stopped, then the ViewModel will be cleared as well.
Solution 2 - Android
Check method onDestroy()
in Fragment.java
public void onDestroy() {
this.mCalled = true;
FragmentActivity activity = this.getActivity();
boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
if (this.mViewModelStore != null && !isChangingConfigurations) {
this.mViewModelStore.clear();
}
}
The variant isChangingConfigurations is true when the Activity rotates, the viewModelStore method clear() is not called.
When Activity is destroyed, isChangingConfigurations is false, the viewModelStore will be cleared.
Solution 3 - Android
Through the source code, we know the ViewModel binds with HolderFragment. you can from the code in class ViewModelProviders to find it.
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
next, in-class HolderFragment on it's onDestroy()
you can find
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
Last, open it,
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
now, maybe you have know it. just like the picture above. When the fragment finished, it cleared; when activity recreate,the fragment's onDestroy() will not be invoked, because
public HolderFragment() {
setRetainInstance(true);
}
hope it can help you.
Solution 4 - Android
If you follow the trail (Check super class) AppCompatActivity --> FragmentActivity --> ComponentActivity
ComponentActivity observe the lifecycle state.
onDestory() calls at configuration change (such as screen rotation) but viewModel doesn't get destroy because of the following condition.
getLifecycle().addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
Solution 5 - Android
I wanted my VM's onClear to be called when the Activity
was finishing. I use onPause
, because the call to onDestroy
is not always immediately executed...it could be a few seconds after onPause
:
class SomeActivity : AppCompatActivity() {
override fun onPause() {
super.onPause()
// viewmodel is not always cleared immediately after all views detach from it, which delays
// the vm's cleanup code being called, which lets the resources continue running
// after all UIs detach, which is weird, because I was using timers and media players.
// this makes the VM execute onCleared when its Activity detaches from it.
if (isFinishing) {
viewModelStore.clear()
}
}
}