So, I have implemented single activity with multiple fragments pattern using Navigation. I used viewmodel for each fragment for non-ui operatios.
The problem is when you navigate using findNavController().navigate()
, the fragment is not actually destroyed. Only the onDestroyView
is called. So, the fragment's onDestroy never gets called and subsequently the viewmodel doesn't get cleared and so the LiveData observer also remains alive and when i come back to the fragment the observer is created again and so live data is observed twice. Once with the old data that it holds and second with the new data from some operations.
For example, I have fragmentA and fragmentB
A shows a list and B you can add something which will be shown in the list. maybe fetching new data from api in fragment B to be shown in A.
So, when i go back from fragment B to A the observer gets called twice first with old data then second with the updated data. In the end the list shows correct data but I don't want two observers happening.
I have followed this article https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808
and tried to use viewLifeCycleOwner
instead of this
but that does not help and issue still exists.
I also tried removing observer before observing :
vm.ld.removeObservers(this)
vm.ld.observe(viewLifeCyclerOwner, observer)
still the issue remains.
(I tried removing observer in onDestroyView
also, still the issue remains.)
The only work around i found is manually calling the viewmodel onCleared
in onDestroyView
and clearing the livedata.
In fragment onDestroyView
vm.clear()
In viewmodel
fun clear() = onCleared()
override fun onCleared() {
//do stuff
}
Now, this solves my issue. But i feel this is not a solid solution and there can be a better way to do this. I would be glad if anybody can shed a light on this one. Thanks.
Retain an object during a configuration change In such a situation, you can alleviate the burden of reinitializing part of your activity by using a ViewModel . ViewModel objects are preserved across configuration changes so they are the perfect place to keep your UI data without having to query them again.
ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity or fragment instance. FYI: You can use ViewModel to preserve UI state only during a configuration change, nothing else as explained perfectly in this official doc.
It is because of the Android system destroys the activity and recreates it from scratch, and as the activity is recreated, the old resources and instances of various classes(ViewModelProvider in our case) are first destroyed on calling onDestroy() method and later recreated in onCreate() .
ViewModel is responsible for holding and processing all the data needed for the UI. It should never access your view hierarchy (like view binding object) or hold a reference to the activity or the fragment.
I wasted a couple of days troubleshooting a similar issue. Double check how your VM is initialized:
val myVm: MyViewModel by activityViewModels()
vs.
val myVm: MyViewModel by viewModels()
If you use the by activityViewModels()
delegate, you are instructing Android to tie the lifetime of the VM to the host activity rather than the current Fragment. Thus, your VM won't be cleared even when the fragment is destroyed. I learned this the hard way. Switching back to the by viewModels()
delegate leaves the VM scoped to the lifetime of the Fragment. When the fragment is destroyed, if it's the only observer, the VM will clear.
I was totally confused by the this
vs. viewLifecycleOwner
target when observing. Apparently, the choice of target has only to do with whether you intend to manually control presenting the dialog of a DialogFragment. Another gem of confusion.
In your case, if you're switching between fragments and onDestroy
isn't being called, it could also be because fragments are being retained. For example, ViewPager2
has offscreenPageLimit
, which instructs Android to keep hidden fragments in memory as you switch pages, adding even further to this mess of having to know absolutely everything about everything just to use the SDK.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With