how to instantiate ViewModel In AndroidX?
Updating ViewModel to Lifecycle Version 2.2.0 and Above
The ViewModels (VMs) may theoretically be initialized as class level instance variables using the Kotlin extension library import androidx.fragment.app.viewModels
method by viewmodels()
. By initializing the VM as a class level instance var it can be accessed within the class.
Question: Is there a downside to initializing the VMs as class level instance variables instead of inside onCreate
?
When creating the VMs with the extension function inside onCreate
the VMs are only scoped within onCreate
and extra code is required to reassign the class level instance variables.
See documentation
- ViewModel Overview
- Lifecycle
Initialize VM as Class Instance Val
class Fragment : Fragment() {
private val viewModel: SomeViewModel by viewModels()
private fun observeViewState() {
viewModel.feedViewState.observe(viewLifecycleOwner) { viewState ->
//viewState used here.
}
}
}
Initialize VM in onCreate and Reassign Class Instance Var
class Fragment : Fragment() {
private lateinit var viewModel: SomeViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: ContentViewModel by viewModels()
this.viewModel = viewModel
}
private fun observeViewState() {
viewModel.feedViewState.observe(viewLifecycleOwner) { viewState ->
//viewState used here.
}
}
}
Passing Arguments/Parameters
// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
}
class SomeViewModel(private val someString: String) : ViewModel() {
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") }
}
Enabling SavedState with Arguments/Parameters
class SomeViewModelFactory(
private val owner: SavedStateRegistryOwner,
private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
SomeViewModel(state, someString) as T
}
class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
if (position == null) 0 else position
}
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
fun saveFeedPosition(position: Int) {
state.set(FEED_POSITION_KEY, position)
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") }
private var feedPosition: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
feedPosition = someViewModel.feedPosition
}
}
For me, the only thing that worked:
implementation 'androidx.fragment:fragment:1.2.4'
Updated answer:
Things changed a little bit, as the previously needed dependency - ViewModelProviders
- got deprecated (see the old answer for details). You can now use the ViewModelProvider
constructor directly.
So, in this case, the answer would be:
private val viewModel = ViewModelProvider(this).get(SheduleViewModel::class.java)
Note that, however, if you include the androidx.activity:activity-ktx:$Version
dependency (a few of the commonly used AndroidX dependencies already include it for you), you can make use of property delegation:
private val viewModel: SheduleViewModel by viewModels()
Which internally will use ViewModelProvider
and scope your ViewModel
to your Activity
. It's just a more concise way of writing the same thing. You can do the same for a Fragment
by including the androidx.fragment:fragment-ktx:$Version
dependency instead (again, commonly already included by other AndroidX dependencies).
Both the ViewModelProvider
constructor and by viewModels()
also accept a factory as a parameter (useful for injecting your ViewModel
):
private val viewModel =
ViewModelProvider(this, viewModelFactory).get(SheduleViewModel::class.java)
and
private val viewModel: SheduleViewModel by viewModels { viewModelFactory }
Use the one that best suits you.
Old answer:
Add the androidx.lifecycle:lifecycle-extensions:$lifecycleExtensionsVersion
dependency in order to import ViewModelProviders
.