94

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

ir2pid
  • 5,604
  • 12
  • 63
  • 107

6 Answers6

73

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.

AdamHurwitz
  • 9,758
  • 10
  • 72
  • 134
Sagar
  • 23,903
  • 4
  • 62
  • 62
  • 34
    since the app lifecycle callbacks, onPause->onStop->onDestroy is same for both rotating and when an Activity ends, how is a ViewModel figuring out internally the right time to call onCleared and finally end its lifecycle. – ir2pid Jun 22 '18 at 13:58
  • 5
    @ir2pid You can check out [this blog](https://android.jlelse.eu/deep-dive-inside-of-androids-viewmodel-architecture-components-e6756dc0bb11) for intenral implementation details – Sagar Jun 22 '18 at 14:04
  • In `ComponentActivity` there is a lifecycle observer, which clears the viewmodel store only when the orientation is not changed. – Balaji Sep 12 '22 at 11:19
  • 1
    onCleared is NOT called when the activity / fragment is destroyed. It is only called when the current activity/fragment is popped from the Back stack. If you are on the home screen and get an orientation change, the activity/fragment gets destroyed but onCleared in the viewmodel will not be called. Navigate to another activity/fragment and onCleared will be called in the viewmodel for that activity/fragment when the Back button is pressed. – Johann Nov 25 '22 at 08:58
39

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.

akhilesh0707
  • 6,709
  • 5
  • 44
  • 51
Eric Y
  • 407
  • 4
  • 3
  • 2
    Which version of Fragment library do you use? In `androidx.fragment:fragment:1.2.5` I cannot find that. Of cource I found method `onDestroy` but no var `isChangigConf`, etc.) It is hidden somewhere else, I guess. – Vít Kapitola Jul 17 '20 at 12:26
  • @VítKapitola: check `ComponentActivity` or `FragmentManager` – Huy Nguyen Sep 30 '20 at 09:37
  • 1
    @HuyNguyen I didn't find it either in `ComponentActivity` or `FragmentManager`... – Vít Kapitola Oct 06 '20 at 12:01
  • Thank you so much for this answer. I was searching about how ViewModel gets to know that this is the time to destroy/clear i.e. when does it knows if activity if finishing permanently or temporarily. – Mohit Rajput Jun 30 '22 at 12:25
  • @VítKapitola Yes it is there in ComponentActivity https://stackoverflow.com/a/55867798/2685454 – Sumit Jul 26 '23 at 13:22
6

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.

akhilesh0707
  • 6,709
  • 5
  • 44
  • 51
Longalei
  • 453
  • 3
  • 8
4

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();
                    }
                }
            }
        });
sajal
  • 169
  • 10
2

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()
        }
    }
}
Eric
  • 16,397
  • 8
  • 68
  • 76
  • If a user receives a dialog that causes your activity to be paused, you'd be destroying the viewmodel as well; this is not forbidden, but I would frown really really hard if I saw this in production code. – Martin Marconcini Feb 25 '21 at 09:33
  • @MartinMarconcini is `isFinishing` true in those cases? i did some tests, like requesting permissions, and getting the system permission dialog to show up. in that case, `onPause` is called, but `isFinishing` is false, so the VM is not cleared. I also tried showing a `DialogFragment`, but `onPause` is not called in that case – Eric Feb 25 '21 at 18:59
  • Ahh good point, i'm not sure to be honest, I'd have to look at the latest code that _triggers_ the onCleared() on the ViewModel but there were two conditions for that to normally happen: `onDestroy` *and* a boolean called `isOrientationChange` or similar (basically if you "rotated" your device, then the viewModel won't be cleared). I think you're good with that, it's just that in my eyes, it somehow defeats the purpose of the convention of viewModels and I never had a reason to change that, that's why I'd frown. I'm not implying this is bad or forbidden in any way. I think you're safe. – Martin Marconcini Feb 26 '21 at 08:43
0

And here is the order of execution in respect to the GenericLifecycleObserver:

  1. onStateChanged()
  2. onResume()/onDestroy()/etc.

Meaning the observer received the information about the pending state change before it's completed, so for exemple onDestroy() method is finished.

Malachiasz
  • 7,126
  • 2
  • 35
  • 49