Skip to main content

Command Palette

Search for a command to run...

Recommended Ways To Create ViewModel or AndroidViewModel

Kotlin examples to show different ViewModel and AndroidViewModel implementations

Updated
5 min read
Recommended Ways To Create ViewModel or AndroidViewModel
V

I'm a self-taught hobbyist Android developer who loves to build projects and share valuable tips for new Android developers.

Feel free to comment, share or connect with me!

There are few ways to create ViewModel and AndroidViewModel. This article shows you the Kotlin examples of creating them.

This is an example of ViewModel or AndroidViewModel class that you may have.

class MyViewModel: ViewModel() {
}
class MyAndroidViewModel (app: Application)
    : AndroidViewModel(app) {
}

The code examples here are used in fragment class. So it may not work in the activity class. Small modifications are required if you copy and paste them into your activity class.

If you're not familiar Kotlin, you can go through some quick examples here first to understand some important concepts such as "Delegation".

Manual Creation - Don't do this!

private val viewModel = MyViewModel()
private val androidViewModel = 
    MyAndroidViewModel(requireActivity().application)

This works only if you don't rotate your phone. When you rotate your phone, an activity or fragment is destroyed and recreated. A new instance of ViewModel or AndroidViewModel is created again. So all the data before the screen rotation is lost. This defeats the purpose of ViewModel architecture. You want ViewModel to survive through activity or fragment destruction.

I made this mistake because I did not understand the reason of usingViewModelProvider to create ViewModel

lateinit var with ViewModelProvider

private lateinit var viewModel: MyViewModel
private lateinit var androidViewModel: MyAndroidViewModel
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {

    viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    androidViewModel =          
        ViewModelProvider(this).get(MyAndroidViewModel::class.java)
}

Using ViewModelProvider is the right way to create ViewModel. When the activity or fragment is created, ViewModelProvider is smart enough to figure out to reuse the first created ViewModel instance.

If ViewModel doesn't change (which is likely true), using val Kotlin variable is a better option here.

by lazy with ViewModelProvider

To use val variable, you use by lazy property initialization. The delegated block gets executed when the variable is first accessed.

private val viewModel: MyViewModel by lazy {
    ViewModelProvider(this).get(MyViewModel::class.java)
}

private val androidViewModel: MyAndroidViewModel by lazy {
    ViewModelProvider(this).get(MyAndroidViewModel::class.java)
}

The code looks a lot cleaner than lateinit var solution. However, another elegant way is to use by viewModels or by activityViewModels.

by viewModels / activityViewModels

To use this Property Delegation, the following dependency needs to be added to the build.gradle (module-level). The version is just an example, you can use later or latest version.

implementation 'androidx.fragment:fragment-ktx:1.3.6'

The following code is awesome! It essentially does the same thing as by lazy without the need to specify the ViewModelProvider. It automatically figures out that for you.

private val viewModel: MyViewModel by viewModels()
private val androidViewModel: MyAndroidViewModel by viewModels()

If you want to share your ViewModel across different fragments within the same activity. You can use by activityViewModels.

private val viewModel: MyViewModel by activityViewModels()
private val androidViewModel: MyAndroidViewModel 
    by activityViewModels()

by viewModels (Custom Constructor Parameter)

It is very common to pass additional objects to the ViewModel constructor. The following example is passing Repository object into the MyViewModel and MyAndroidViewModel.

class MyViewModel(private val repository: Repository)
    : ViewModel() {
}

class MyAndroidViewModel(app: Application, repository: Repository)
    : AndroidViewModel(app) {
}

Having a custom constructor parameter for ViewModel is a bit complicated. You need to have a custom ViewModel factory to create your ViewModel.

To create your custom ViewModel factory, you can inherit from ViewModelProvider.NewInstanceFactory.

class MyViewModelFactory(private val repository: Repository)
    : ViewModelProvider.NewInstanceFactory() {

    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")
    }
}

For custom AndroidViewModel factory, you can inherit from ViewModelProvider.AndroidViewModelFactory

class MyAndroidViewModelFactory(
    private val app: Application,
    private val repository: Repository)
    : ViewModelProvider.AndroidViewModelFactory(app) {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {

        if (modelClass.isAssignableFrom(
                MyAndroidViewModel::class.java)) {

            return MyAndroidViewModel(app, repository) as T
        }

        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

[Updated - Oct 30, 2021]: In fact, we can just implement the ViewModelProvider.Factory interface for both MyViewModelFactory and MyAndroidViewModelFactory. Examples can be found here.

To create ViewModel with your custom constructor parameter, you use by viewModels delegate property.

private val viewModel: MyViewModel by viewModels {

    MyViewModelFactory(Repository())
}

private val androidViewModel: MyAndroidViewModel by viewModels {

    MyAndroidViewModelFactory(
        requireActivity().application,
        Repository())
}

You can replace by viewModels with by ActivityViewModels if you want your ViewModel to survive in different fragments within the same activity.

private val viewModel: MyAndroidViewModel by activityViewModels {

    MyViewModelFactory(Repository())
}

private val androidViewModel: MyAndroidViewModel 
    by activityViewModels {

        MyAndroidViewModelFactory(
            requireActivity().application,
            Repository())
    }

[Updated - Nov 7, 2021]: you can also use by lazy and ViewModelProvider() instead of by viewModels and it should still work. It can't be used to replace by activityViewModels because the created ViewModelwon't be shared across different fragments. So this is just for your reference and knowledge.

private val viewModel: MyViewModel by lazy {
    val factory = MyViewModelFactory(Repository())

    ViewModelProvider(this, factory).get(MyAndroidViewModel::class.java)
}
private val viewModel: MyAndroidViewModel by lazy {
    val factory = MyAndroidViewModelFactory(
        requireActivity().application, 
        Repository())

    ViewModelProvider(this, factory).get(MyAndroidViewModel::class.java)
}

My Common Practices

The fun thing about programming is there are many ways to do the same thing. Understand the differences, make you a better programmer.

I use the last method - by viewModels (Custom Constructor Parameter) by default because I usually have custom constructor parameters in my ViewModel.

Also, I usually use by activityViewModels instead of by viewModels which allows me to share data across different fragments. It saves my time to figure out how to pass data to different fragments. For example, using Bundleto share data between fragments.

I also use AndroidViewModel by default instead of ViewModel because I usually need to access string resources and system services from the Application context. There are drawbacks being discussed over the internet, but I do not fully understand this part yet. For now, AndroidViewModel is good for me.

These are my common practices. I'm not sure other Android developers agree with me. Let me know your thoughts.

[Updated - July 15, 2022]: I managed to try hilt to inject the dependencies ito view model. Here is the example:

Y
YT Kanal3y ago

I tried to implement your solution but it does not seem to work. Do you have any idea what Im doing wrong?

What I did: Added implementation "androidx.fragment:fragment-ktx:1.3.6"

My Android View Model constructor looks like this:

class MainViewModel(private val app: Application, webApiRepository: IWebApiRepository = WebApiRepository.getInstance(app)) : ViewModelProvider.AndroidViewModelFactory(app)

I also added the create function:

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(
                MainViewModel::class.java)) {

            return MainViewModel(app, webApiRepository) as T
        }

        throw IllegalArgumentException("Unknown ViewModel class")
    }

But when I try to use by activityViewModels or viewModels it does not work:

(Remove the two empty spaces) https:// cdn.hashnode .com/res/hashnode/image/upload/v1657015361719/7gYkfO5k8.png

I need to use activityViewModel in order to get a shared ViewModel across fragments. Thanks

2
V

Did you name your factory class wrongly? It should be something like MainViewModelFactory instead of MainViewModel. Maybe it conflicts with your MainViewModel class, thus it is not working. Just guessing. :)

1
Y
YT Kanal3y ago

Vincent Tsen You are the man! Im an idiot, didnt understand I had to create new seperate class haha

1
D

Thank you for the article, it was very helpful for me!

If just want to mention I had to remove "?" on line "override fun <T : ViewModel?> create(modelClass: Class<T>): T {" in order to remove a "'create' overrides nothing" error

class MyAndroidViewModelFactory(
    private val app: Application,
    private val repository: Repository)
    : ViewModelProvider.AndroidViewModelFactory(app) {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {

        if (modelClass.isAssignableFrom(
                MyAndroidViewModel::class.java)) {

            return MyAndroidViewModel(app, repository) as T
        }

        throw IllegalArgumentException("Unknown ViewModel class")
    }
}
1
V

You're welcome, I'm glad it helps. It looks like the API has changed, I will fix that. Thanks for pointing that out.

1
C
Craig4y ago

Useful info, thanks. But I'm wondering if there's a way to use the view models to share data between an activity and the fragments it contains, or is it only possible between fragments?

I can't find a way to get an activity to be able to set up a view model that the fragments also use if I need a custom factory. They all seem to need the factory to be provided, which then means that the factory has to be provided via some other means. If there are multiple additional arguments then that is slightly better, but otherwise the shared data might just as well be passed more direclty. Even if the factory is passed it's not clear that the same view model would be used rather than the factory being used to construct separate instances.

1
V

Craig You're welcome! Have you tried by activityViewModels? If both fragments use this, only one ViewModel is created. It works with custom ViewModel factory too.

It supposes to work. You save the data in View Model before you navigate to the next fragment, and you retrieve the data from the ViewModel in another fragment. I have done this in one of my apps.

Another way is using Dependency Injection, and I haven't tried this out yet. This seems like a better approach than the custom ViewModel factory.

In fact, if we want to do this properly, I think each fragment should have their own independent ViewModel. There shouldn't share the same ViewModel. That data is shared by using Safe Args which allows you to save the data into the bundle and retrieve it from the target fragment during the navigation. I have tried this too, but by acitivtyViewModels is a lot more convenient.

I'm not sure if I answer your questions, but I hope it helps, and thanks for the comment.

V

By the way, I just noticed I mentioned to use by Lazy to replace the both by ViewModels and by ActivityViewModels which is NOT correct.

It can be used to replace by ViewModels only, and NOT to be used to replace by AcitivityViewModels.

I will fix it.

Android App Dev

Part 47 of 47

Discover helpful tips and tricks for Android app development and Jetpack Compose through my personal journey and experience.

Start from the beginning

Convert KAPT to KSP - Room and Hilt Examples

Step-by-Step Guide: Migrating from KAPT to KSP for Room Database and Hilt Dependency Injection