To expand on Arda Kazancı's answer, the back stack is a record of the navigation history within a NavController
.
Each time you navigate to a Fragment
, that Fragment
is added to the back stack.
When you navigate back, the current Fragment
is popped off the back stack, and the previous Fragment
is recreated.
However, if you are scoping your ViewModel
to the NavController
's lifecycle, the ViewModel
will not be recreated along with the Fragment
; instead, the existing ViewModel
instance will be reused.
When we talk about scoping a ViewModel
to a Fragment
's lifecycle or to a NavController
's lifecycle, we are talking about when the ViewModel
is created and destroyed.
If the ViewModel
is scoped to the Fragment
's lifecycle (using viewLifecycleOwner
), the ViewModel
will be created when the Fragment
's view is created and destroyed when the Fragment
's view is destroyed. This is true whether the Fragment
is destroyed because it was popped off the back stack or because of a configuration change like a screen rotation.
If the ViewModel
is scoped to the NavController
's lifecycle (the default behavior when using ViewModelProvider(this)
in a Fragment
), the ViewModel
will be created the first time the Fragment
is launched and will not be destroyed until the NavController
is destroyed (which usually happens when the activity hosting the NavController
is finished). This means that the ViewModel
will survive even if the Fragment
is destroyed and recreated, such as when the user navigates away from the Fragment
and then back to it, causing the Fragment
to be popped off the back stack and then added back to it.
When you are using the Navigation component along with NavHostFragment
, it uses a slightly different lifecycle scope for the ViewModel
than a standalone Fragment
would. In other words, even though your Fragment
is being recreated, the associated ViewModel
is not.
(See also "Get started with the Navigation component")
This is because NavHostFragment
maintains its own ViewModelStore
that is scoped to the NavController
's lifecycle. This ViewModelStore
is used to store ViewModels
for all destinations (fragments) that are a part of the NavController
.
(See also "Communication of Fragments in the Fragment, using Navigation Component, MVVM and Koin" by Piotr Prus)
When you are calling ViewModelProvider(this).get(DashboardViewModel::class.java)
, the ViewModel
is being retrieved from the ViewModelStore
associated with the NavController
, not the individual fragment. This means that the ViewModel
will persist as long as the NavController
is alive, even if individual fragments are destroyed and recreated.
This design allows data to be persisted across configuration changes and navigation events, which is a common pattern in Android development.
You can observe this behavior by checking the ID of the ViewModel
object:
Log.i("CHEOK", "DashboardViewModel constructed. ID: ${System.identityHashCode(this)}")
You will see that the ID remains the same across different instances of the Fragment
, indicating that it is the same ViewModel
instance.
If you want a ViewModel
that is scoped to the NavController
's lifecycle and survives configuration changes and navigation between Fragments
, you can use navGraphViewModels
:
class DashboardFragment : Fragment() {
private val dashboardViewModel: DashboardViewModel by navGraphViewModels(R.id.nav_graph) { ViewModelProvider.AndroidViewModelFactory.getInstance(activity?.application!!) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Use the ViewModel
}
}
Here, R.id.nav_graph
is the ID of your navigation graph. This ViewModel
will be shared between all Fragments
in the navigation graph and will be destroyed when the last Fragment
in the navigation graph is popped off the back stack.
(See also "What is the difference between navGraphViewModels and activityViewModels?")
But then, the original behavior you observed will persist. The DashboardViewModel
will not be re-constructed each time you navigate back to the DashboardFragment
.
The navGraphViewModels
delegate method creates or retrieves a ViewModel
that is scoped to a navigation graph. This means the ViewModel
will be shared between all fragments in the navigation graph and will survive configuration changes and navigation between fragments. The ViewModel
will only be cleared (and thus ready for re-construction) when the last fragment in the navigation graph is popped off the back stack.
If you want the ViewModel
to be scoped to the Fragment
's lifecycle instead of the NavController
's lifecycle, you would have to use the ViewModelProvider
with the Fragment
's viewLifecycleOwner
:
val dashboardViewModel = ViewModelProvider(viewLifecycleOwner).get(DashboardViewModel::class.java)
Note: as noted in "ViewModelProviders is deprecated in 1.1.0", early in 2020, Google had deprecated the ViewModelProviders class, in version 2.2.0 of the AndroidX lifecycle library.
In the context of the AndroidX libraries, the equivalent would be:
dashboardViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(activity?.application!!)).get(DashboardViewModel::class.java)
With this
referring to the Fragment
itself. ViewModelProvider.AndroidViewModelFactory.getInstance(activity?.application!!)
is the Factory
that is used to create the ViewModel
. This factory requires the Application
as a parameter, and it is retrieved from the Fragment
's associated Activity
.
Those codes will give you a ViewModel
that is scoped to the Fragment
's lifecycle and will be recreated every time the Fragment
is recreated.
However, keep in mind that this might not be the desired behavior, especially if you want to maintain state across navigation events.
Illustration: "How Android ViewModel works under the hood to survive to configuration change" by Torcheux Frédéric.