13

Using Android X and the Android Architecture Components, namely the following...

  • Navigation
  • Lifecycle
  • ViewModel

... has made Android development much easier for better and more stable apps.

When using a ViewModel exposing LiveDatas for a Fragment, there are currently two LifecycleOwners that can be used to oberve:

  1. the Fragment itself or
  2. the Fragment's property mViewLifecycleOwner.

Up until now, I have always and exclusively use the Fragment itself for any and all LiveData exposed by ViewModels.

As I like to live on the edge by frequently upgrading to the latest alphas & betas, I have recently upgraded to:

  • navigation ktx version 2.2.0-rc01 ;
  • activity ktx version 1.1.0-rc01 ;
  • fragment ktx version 1.2.0-rc01 ;
  • lifecycle ktx version 2.2.0-rc01 ;

OK luckily, at this date (1st nov 2019), there were all available as release candidates.

This had the consequence to warn me in old java code:

enter image description here

How come there is actually two LifecycleOwners ?

Should I also follow this warning when setting the lifecycle owner of the Fragment's databound layout (using the Databinding library) ?

Then what's the use for the Fragment itself as a LifecycleOwener?

Mackovich
  • 3,319
  • 6
  • 35
  • 73
  • 1
    I'd argue that ViewModel did not help create more stable apps, because it has no direct `onSaveState/onRestoreState` support, and people are unaware of the viewmodel-savedstate module that provides the `SavedStateHandle`. – EpicPandaForce Jan 05 '20 at 16:58

1 Answers1

51

There are two different lifecycles because the Fragment itself lives longer than the Fragment's view.

There are a number of events that can cause the Fragment's view to be destroyed, but currently keep the Fragment itself alive:

  1. Putting the Fragment on the back stack (i.e., when you navigate() to another Fragment)
  2. Calling detach() on a Fragment
  3. Calling setMaxLifecycle(Lifecycle.State.CREATED) on a Fragment

In these cases, the Fragment's view is destroyed, moving the Fragment view lifecycle to DESTROYED. However, the lifecycle of Fragment itself is not destroyed - it generally stays as CREATED. This means that the Fragment can go through multiple cycles of onCreateView() -> onViewCreated() -> onDestroyView() while only going through onCreate() once.

Where this becomes a problem is when it comes to how LiveData works. When you observe a LiveData, LiveData automatically unregisters the observer when the lifecycle reaches DESTROYED. But if you use the Fragment's lifecycle to observe in onCreateView(), etc., that registered observer will still exist after onDestroyView(), despite the view being destroyed. This means that the second time your Fragment goes through onCreateView(), you'll actually create a second active Observer, with both running simultaneously. Then three Observers the next time and on and on.

By using the view LifecycleOwner in onCreateView()/onViewCreated(), you ensure that you'll only have one active Observer running at a time and that Observers tied to previous view instances are correctly destroyed along with the view. Therefore, yes, you should always use getViewLifecycleOwner() as the LifecycleOwner when in onCreateView() or onViewCreated(), including when using Data Binding.

Of course, if you're registering an Observer in onCreate(), then the view LifecycleOwner does not exist yet (it is created right before onCreateView()) and you don't have the multiple registration problem, which is why the Lint check specifically does not apply to any registrations done at onCreate() time. In those cases, using the Fragment's lifecycle itself is absolutely correct.

As per the Fragments: Past, Present, and Future talk, one future improvements for Fragments is going to be combining the two lifecycles together, always destroying the Fragment whenever the Fragment's view is destroyed. This is not yet available in any shipped version of Fragments, alpha or otherwise.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • Thank you for this detailed answer. All in all, am I to believe there is almost no point in using the `Fragment` itself as a `LifecycleOwner` ? – Mackovich Nov 03 '19 at 20:26
  • 1
    @Mackovich - like I said, you can `observe` or add your own `LifecycleObserver` in `onCreate()`. Not everything lifecycle related is solely about filling your UI. – ianhanniballake Nov 03 '19 at 20:34
  • What about `DialogFragment`s? Is it always fine to not use the `viewLifecycleOwner` there? – Fred Porciúncula Jan 11 '20 at 15:56
  • 1
    @FredPorciúncula - DialogFragments don't go on the back stack, so their lifecycle ~always change at the same time. – ianhanniballake Jan 11 '20 at 16:25
  • @ianhanniballake is there a way to handle the setmaxstate of the first fragments that are being loaded in the multiple back stack sample you preseted in Past/Present/future. I am asking because in that sample all fragments that are being added to a NavHost their state is set as setMaxLifecycle(Lifecycle.State.Resumed), this means that all the fragmets are going to run their viewmodel logic, calling rest services, etc, observe the changes, update the view, in all fragments that are setted as starting destination of the nav graphs, on the first load of the application. Any advice will be great – Catluc Mar 03 '20 at 23:06
  • @Catluc - you can call `setMaxLifecycle()` in your copy of `NavigationExtensions`, sure. – ianhanniballake Mar 03 '20 at 23:14
  • @ianhanniballake thnks for the advice saddly its not working, because I belive it has something to do with this NavHostFragment.create(navGraphId), under the hood is calling onCreate()/onAttach() and this methods sets up the state of the starting destination fragment of each graph. – Catluc Mar 04 '20 at 00:02
  • @ianhanniballake In the end i found the solution, on setting navHost i had to set setMaxLifecycle to STARTED and when selecting the new navhost (on click on another button from bottomNavigationView) setMaxLifecycle to Resumed. "Its kind of ;)" hackish but it does the job, thank for ur time an advice, hope to see soon the new updates from navigation lib. – Catluc Mar 04 '20 at 00:32