70
votes

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

enter image description here

5

5 Answers

45
votes

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:

ViewModel

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.

25
votes

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.

5
votes

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.

4
votes

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();
                    }
                }
            }
        });
2
votes

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()
        }
    }
}