Reputation:
I'm using the ViewModel
to update the title in the action bar
SharedViewModel
class SharedViewModel @ViewModelInject constructor(
@Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val title: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
val backButton: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>()
}
}
MainActivity observer
@AndroidEntryPoint
...
sharedViewModel.title.observe(this, Observer {
supportActionBar?.title = it
})
Using the code below seems to create a new instance in Fragment
(checked in the debugger):
@AndroidEntryPoint
...
private val viewModel: SharedViewModel by viewModels()
But seems to work this way
val viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
Is this supposed to be done this way or am I doing something wrong?
Thanks!
Upvotes: 2
Views: 6473
Reputation: 4694
If you want to use a shared view model in a fragment you have to use by activityViewModels()
instead of by viewModels()
.
Why does the next line work but by viewModels()
doesn't?
ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
Because by default viewModels()
has its ownerProducer
argument value as { this }
. To understand it better here is the source code:
@MainThread
inline fun <reified VM : ViewModel> Fragment.viewModels(
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
As you can see it is an extension function for Fragment
class. It means that the ViewModelStoreOwner
is the fragment. As soon as the fragment is removed from the stack all view models it stores in view model store are gone.
But if you use by activityViewModels()
you use Activity
as view model store owner. Notice use of requireActivity().viewModelStore
instead of ownerProducer().viewModelStore
which is a Fragment
by default.
@MainThread
inline fun <reified VM : ViewModel> Fragment.activityViewModels(
noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
Eventually, createViewModelLazy
is calling
ViewModelProvider(store, factory).get(viewModelClass.java)
which is the equivalent of you creating view model by hand using
ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
Use by activityViewModels()
:
@AndroidEntryPoint
...
private val viewModel: SharedViewModel by activityViewModels()
Note: by activityViewModels()
and by viewModels()
are just lazy load implementations of the ViewModelProvider(...).get(...)
.
Upvotes: 10