Creating viewmodel instance is needed if you are using MVVM architecture. Otherwise, MVVM is incomplete. So in this post, I will tell you about different ways for creating instance of ViewModel in activity or fragment.
By using correct usage of viewmodel, we can avoid ‘Android cannot create instance of viewmodel’ issue also.
okay, let’s start.
First I will tell you in short. So it will save your time.
- viewModel() – Used in Jetpack Compose.
- by viewModels() – Apply it with Activity or fragment.
- by activityViewModels() – Recommended to use when multiple fragments need to share same data and has same instance of Activity
- Using ViewModelProvider – old way but still works.
- using ViewModelProvider.Factory – Make use of it if you need to pass arguments to viewmodel.
- @hiltViewModel – This is from Hilt dependency injection, very helpful if you are using Hilt.
- hiltViewModel() – Use it in Composables
Below, I have mentioned versions of different dependencies, so if you need different version, please check the maven repository and use the latest one.
Detailed Version With Code
- viewModel()
In Jetpack Compose, viewModel() delegate can be used for creating instance of viewmodel. But before that you need to include the dependencies in gradle file, only then you can use this. If you want to pass parameters, then go with Hilt or ViewModelProvider.Factory
androidx.Lifecycle:lifecycle-viewmodel-compose:"latest version"(2.8.7 )
2. by viewModels()
val viewModel: MyViewModel by viewModels()
If you are using XML based approach, you can easily create instance of viewmodel using viewModels(). This does not support passing parameters.
3. activityViewModels()
val viewModel: MyViewModel by activityViewModels()
You can use this in your fragment to access instance of viewmodel – better to use when multiple fragment within same activity needs viewmodel instance.
implementation("androidx.fragment:fragment-ktx:1.6.2")
4. Use ViewModelProvider
ViewModelProvider(this).get(MyViewModel::class.java)
As the name suggests ViewModelProvider gives the viewmodel instance by passing class type of the viewmodel.
5. ViewModelProvider.Factory
val repository = MyRepository() val factory = MyViewModelFactory(repository) viewmodel = ViewModelProvider(this, factory)[MyViewModel::class.java]
In most cases, we need to pass a repository or other instances to the viewmodel. So in this example, i am passing a repository to the viewmodel using ViewModelProvider.
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider class MyViewModelFactory(private val repository: MyRepository) :ViewModelProvider.Factory{ override fun <T : ViewModel> create(modelClass: Class<T>): T { if(modelClass.isAssignableFrom(MyViewModel::class.java)){ return MyViewModel(repository) as T } throw IllegalArgumentException("Unknown ViewModel class") } }
Here, the subclass of ViewModelProvider.Factory will receives the instance of repository and creates viewmodel instance for you.
5. Using @HiltViewModel
This approach can be used if you are Hilt dependency injection in your project. But you need to do some changes here, not easy like above steps,
1. You need to add dependency in gradle file.
2. Define subclass of Application and annotate with @HiltAndroidApp.
3. Annotate viewmodel class with @HiltViewModel.
4. if you want to access instance of viewmodel in activity or fragment, annotate those with @AndroidEntryPoint
// id 'com.google.dagger.hilt.android' version '2.48' apply false // Dagger - Hilt implementation "com.google.dagger:hilt-android:2.48" kapt "com.google.dagger:hilt-android-compiler:2.48" kapt "androidx.hilt:hilt-compiler:1.0.0"
- These are the dependencies you need to add in gradle file.
@HiltAndroidApp class MyApplication: Application()
- Create a subclass of Application like above and Use @HiltAndroidApp annotation.
@HiltViewModel class MainViewModel @Inject constructor( private val repository: Repository, application: MyApplication ): AndroidViewModel(application)
- If there is any parameters need to pass, create a module and pass from there. So internally hilt will manage. Use @Inject to injecting dependencies through constructor.
@AndroidEntryPoint class MyFragment : Fragment() { private val mainViewModel: MainViewModel by viewModels()
hiltViewModel() - call in composable if you are using hilt.
- Access like above your viewmodel, do not forget to annotate activity or fragment with @AndroidEntryPoint where you are calling viewmodel.
This is for now, may be I will update later.
Thanks.