Shared ViewModel's onCleared() is never called
Asked Answered
B

4

11

I have a single Activity application. In one part of the app there is a master-detail combination of Fragments that use a shared ViewModel to share some data between them. The problem I face is that the ViewModel's onCleared() method is not called even when both of the Fragments have been destroyed. onCleared() is only called when the activity is destroyed.

Is this how it is supposed to work? Because that is quite useless in a single Activity model, because the Activity is always alive. Or am I missing something?

Beccafico answered 21/2, 2019 at 7:32 Comment(0)
R
8

It would be a little hard to give a solid answer without seeing a little bit of code. My first guess would be that you might have scoped the ViewModel to your Activity and not the Fragment's themselves.

//inside of fragment onCreate()

//scoped to fragment
viewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

//scoped to activity
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)

If this is the case, if you look at the diagram for the scope of a ViewModel. Then the reason why onCleared() never gets called is because your Activity has never technically gotten destroyed since it is what keeps your app on the foreground.

enter image description here

If this isn't the right solution to your problem, then I think the docs on ViewModel might be a good place to start looking for the right answer. Happy coding!

Recurrent answered 21/2, 2019 at 7:50 Comment(3)
Indeed I did. Although instead of requireActivity() I pass parentFragment, but the result is the same, since the parent is the Activity for all the Fragments. If I pass the fragments themselves then there is no sharing of data, because each Fragment creates it's own ViewModel. I guess a shared ViewModel is not a solution to my initial problem.Beccafico
Yeah, unfortunately I think if you are needing onCleared() to be called in a shared ViewModel that can really only happen when the activity is destroyed. What exactly did you need to execute in onCleared()? There might be some other solutions to your problemRecurrent
I have some shared data that is needed by all the Fragments that are accessed from one of the top level destination in my navigation. However once the user leaves this top level destination for another, then this data is no longer needed and should be cleared, so it doesn't use up the memory. I taught ViewModels could save this case, but I guess I will just write something on my own, to share data through the Activity with the option to manually clear the shared data.Beccafico
R
1

If you pass in that activity in ViewModelProviders.of(this), then, yes, that is the expected behaviour. To have a ViewModel scoped only to the master and detail fragments, you would likely need to create a parent fragment for them, e.g., a MasterDetailFragment, which hosts both a MasterFragment and a DetailsFragment.

Redblooded answered 21/2, 2019 at 7:38 Comment(1)
It is better to use KTX extensions and by viewModelsMichalemichalski
B
0

As the link of a shared ViewModel from the original question has been updated, but the answers here is not updated. So this is an updated answer based on Andrew Steinmetz's answer.

Below is the way to create that shared ViewModel from Google's doc:

private val model: SharedViewModel by activityViewModels()

Because it is using activityViewModels, the ViewModel will have an activity scope. It will be stored in its parent activity's viewModelStore after it's being created at first time usage and the ViewModel's onCleared() will be called when the activity is finished.

If the ViewModel is created as below,

private val model: SharedViewModel by viewModels()

The ViewModel will have only the scope as its fragments, because it is stored in its fragments' own viewModelStore, it can survive during the configuration change like screen rotation, but when all the fragments using it are finished. It will be destroyed also.

Bowsprit answered 27/9, 2020 at 8:11 Comment(0)
B
0

It depends upon the scope you are using the ViewModel.

private val viewModel: F1ViewModel by activityViewModels()

if using above scope of activityViewModels(), onCleared() will be called only after onDestroy() of activity

private val viewModel: F1ViewModel by ViewModels()

if using above scope of ViewModels() inside a fragment, onCleared() will be called only after onDestroy() of the fragment.

Bargain answered 5/2, 2022 at 6:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.