The Architecture Components provide default ViewModelProvider implementations for activities and fragments. They allow you to store LiveData instances inside a ViewModel to be reused across configuration changes. The usage with activities is quite straightforward because the activity lifecyle maps well to the Lifecycle interface of the Architecture Components, but the fragment lifecycle is more complex and may cause subtle side effects if you’re not being careful.

The Fragment lifecycle (simplified version)

Fragments can be detached and re-attached. When they are detached, their view hierarchy is destroyed and they become invisible and inactive, but their instance is not destroyed. When they are later re-attached, a new view hierarchy is created and onCreateView() and onViewCreated() are called again.

For this reason, the usually recommended place to initialize Loader s and other asynchronous loading operations that will eventually interact with the view hierarchy is in onViewCreated() . We can assume this is also the best place to initialize LiveData instances by subscribing a new Observer . Most of the official Architecture Components samples also do it there. You would expect typical code to look like this:

class Page1Fragment : Fragment() {



private var textView: TextView? = null



override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

val view = inflater.inflate(R.layout.fragment_page1, container, false)

textView = view.findViewById(R.id.result)

return view

}



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

val vm = ViewModelProviders.of(this)

.get(Page1ViewModel::class.java)



vm.myData.observe(this, Observer { textView?.text = it })

}



override fun onDestroyView() {

super.onDestroyView()

textView = null

}

}

There is a hidden problem with this code. We subscribe an anonymous observer within the lifespan of the fragment (using the fragment as LifecycleOwner ), which means it will automatically be unsubscribed when the fragment is destroyed. However, when the fragment is detached and re-attached, it is not destroyed and a new identical observer instance will be added in onViewCreated() , while the previous one has not been removed! The result is a growing number of identical observers being active at the same time and the same code being executed multiple times, which is both a memory leak and a performance problem (until the fragment is destroyed).

This problem concerns any fragment subscribing an observer for its own lifecycle in onCreateView() or later, without taking any other extra step to unsubscribe it.

Worse: it also impacts retained fragments, which are not destroyed during configuration changes but re-attached to a new Activity.

How do we solve this? There are a few solutions to explore, some better than others. Here are the ones I found so far.

1. Observing in onCreate()

Your first attempt may be to simply move the observer subscription to onCreate() instead of onViewCreated() . But it won’t work as expected without adding more boilerplate code: LiveData keeps track of which result has been delivered to which observer, and it won’t deliver the latest result again to a new view hierarchy because the observer hasn’t changed. This means you have to manually check for a latest result and bind it to the new view hierarchy in onViewCreated() which is not very elegant:

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

vm = ViewModelProviders.of(this)

.get(Page1ViewModel::class.java)



vm.myData.observe(this, Observer(this::bindResult))

}



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)



vm.myData.value?.apply(this::bindResult)

}



private fun bindResult(result: String?) {

textView?.text = result

}

Note that the binding code needs to be called at two different places, so you can’t just write an inline anonymous observer. And finally, there is no way with the current LiveData API to differentiate a null result from no result for the current value, so it’s better not to return any null result (for instance in case of error) when using this solution, which overall is probably the worst one.

2. Manually unsubscribing the observer in onDestroyView()

This is a bit better than the first solution but you still can’t use an inline anonymous observer. You must declare it as a final field, subscribe it in onViewCreated() and also not forget to unsubscribe it in onDestroyView() , so there is still boilerplate code and room for errors.

private val observer = Observer<String?> { textView?.text = it }



override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

vm = ViewModelProviders.of(this)

.get(Page1ViewModel::class.java)

}



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)



vm.myData.observe(this, observer)

}



override fun onDestroyView() {

super.onDestroyView()

textView = null



vm.myData.removeObserver(observer)

}

One of the main benefits of using LiveData being that it takes care of unsubscribing the observer for you, it’s a pity that it has to be done manually in this case. Keep reading, we can still do better.

3. Resetting an existing observer

It’s not actually required to unsubscribe the current observer precisely in onDestroyView() , because it will eventually be unsubscribed automatically in onDestroy() . What’s important is that it’s unsubscribed before an identical one is subscribed in onViewCreated() , in order to avoid duplicates. Therefore, another valid solution is to unsubscribe right before subscribing, using for example this Kotlin extension function:

fun <T> LiveData<T>.reObserve(owner: LifecycleOwner, observer: Observer<T>) {

removeObserver(observer)

observe(owner, observer)

}

Removing and adding back the same observer will effectively reset its state so that LiveData will deliver the latest result again automatically during onStart() , if any. The above function can be used like this:

private val observer = Observer<String?> { textView?.text = it }



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

val vm = ViewModelProviders.of(this)

.get(Page1ViewModel::class.java)



vm.myData.reObserve(this, observer)

}

The fragment code is now less error-prone because there is one less callback to add, but we still need to declare the observer instance as a final field and reuse it, or else unsubscription will silently fail. Despite this, it’s probably my personal favorite solution for a quick workaround.

4. Creating a custom Lifecyle for view hierarchies

Ideally, we would want observers subscribed in onViewCreated() to be automatically unsubscribed in onDestroyView() . This means we actually want to follow the current view hierarchy lifecycle, which is different from the fragment lifecycle. One way to achieve this is to create a custom Fragment which provides an additional custom LifecycleOwner for the current view hierarchy. Here is an implementation in Java.

Thanks to the custom LifecycleOwner returned by getViewLifecycleOwner() , the LiveData observers will be automatically unsubscribed when the view hierarchy is destroyed and nothing else has to be done.

Note: onViewCreated() won’t be called if onCreateView() returns null , so the custom LifecycleOwner won’t be created for headless fragments and getViewLifecycleOwner() will also return null in that case.

With this solution the code is now nearly identical to the initial code sample, but this time it does the right thing.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

val vm = ViewModelProviders.of(this)

.get(Page1ViewModel::class.java)



vm.myData.observe(viewLifecycleOwner,

Observer { textView?.text = it })

}

The downside is that it requires inheriting from a custom Fragment implementation. It is also possible to implement the same functionality without inheritance but then it requires declaring a delegate helper class inside each fragment where that custom LifecycleOwner is needed.

Edit (14 may 2018): Google implemented this solution directly in support library 28.0.0 and AndroidX 1.0.0. All fragments now provide an additional getViewLifecycleOwner() method just like in the above sample, so you don’t need to implement it yourself.

5. Using Data Binding

This section has been rewritten following the release of Android Studio 3.1 and the described solution is considered production-ready.

This last solution provides the cleanest architecture, at the expense of depending on an additional library. It consists of using the Android Data Binding Library to automatically bind your model to the current view hierarchy, and to simplify things the model will be the ViewModel instance already used to contain the various LiveData .

With that architecture, the LiveData instances exposed by the ViewModel will be automatically observed by the generated Binding class instead of the fragment itself.

layout xmlns:android=" xmlns:android=" http://schemas.android.com/apk/res/android "> <data>

<variable

name="viewModel"

type="my.app.viewmodel.Page1ViewModel"

/>

</data> TextView

android:id="@+id/result"

style="

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="@{viewModel.myData}"/>

</layout> android:id="@+id/result"style=" @style/TextAppearance .AppCompat.Large"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@{viewModel.myData}"/>

The layout declares a variable of type Page1ViewModel and binds the TextView ’s android:text property directly to the LiveData field. When the LiveData is updated, the TextView will immediately reflect its new value.

Note: The ability to bind LiveData fields directly to views and make the Binding classes lifecycle-aware has been added in Android Studio 3.1.

The ViewModel will always reflect the latest visual state of the fragment, even when no view hierarchy is currently attached to it. Each time a view hierarchy is created through a Binding class, it will register itself as a new observer on the LiveData instances, so we don’t have to manage any observer manually anymore and we got rid of the problem.

class Page1Fragment : Fragment() {



lateinit var vm: Page1ViewModel



override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

vm = ViewModelProviders.of(this)

.get(Page1ViewModel::class.java)

}



override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

val binding = FragmentPage1Binding

.inflate(inflater, container, false)

binding.viewModel = vm

binding.setLifecycleOwner(this)

return binding.root

}

}

Since the LifecycleOwner is passed to the Binding instance, the LiveData loading logic will still comply with the lifecycle of the current fragment: the views will only be updated while the fragment is started and the internal observers will be properly unsubscribed in onDestroy() .

Furthermore, the special LiveData observers used by the Binding classes use weak references internally so they will eventually be unregistered automatically after their associated view hierarchy has been destroyed and garbage collected, even if the fragment itself has not been destroyed yet.