AndroidViewModel vs ViewModel
AndroidViewModel provides Application context
If you need to use context inside your Viewmodel you should use AndroidViewModel (AVM), because it contains the application context. To retrieve the context call getApplication()
, otherwise use the regular ViewModel (VM).
AndroidViewModel has application context.
We all know having static context instance is evil as it can cause memory leaks!! However, having static Application instance is not as bad as you might think because there is only one Application instance in the running application.
Therefore, using and having Application instance in a specific class is not a problem in general. But, if an Application instance references them, it is a problem because of the reference cycle problem.
See Also about Application Instance
AndroidViewModel Problematic for unit tests
AVM provides application context which is problematic for unit testing. Unit tests should not deal with any of the Android lifecycle, such as context.
What is the Difference between AndroidViewModel and ViewModel in Android Architecture Components?
But for AndoirdViewModel scenario I can get application context by extending a class to Application class
Creating your own custom subclass of Application
does not magically make that singleton instance available to a ViewModel
.
It is possible to create a custom subclass of Application
that has its own getInstance()
method or something to expose the singleton directly. Google does not like this pattern (and neither do I, for that matter), and so Google does not steer developers towards using it.
What is the actual difference between them in Android Development?
A ViewModel
on its own has no good way to get a Context
. AndroidViewModel
supplies an Application
for use as a Context
, and specifically supplies the Application
singleton so we are sure that the Context
itself does not represent a memory leak.
AndroidViewModel vs passing Application context to ViewModel
If you're providing your own factory, you can pass anything you want to a regular ViewModel
object, you're correct about that.
However, if you are using the default factories, the source code shows that the default factories only fill in the Application
instance for you if your ViewModel extend AndroidViewModel
.
whats the difference between viewModel and viewModels class
You compare kotlin delegate function( by viewModels()) and ViewModel class together, open sources i think that will help you better understand.
Why using AndroidViewModel?
Not all codebases are created equal. AndroidViewModel
can be a useful tool for incremental refactoring in "legacy" codebases that don't have many abstractions or layering in place (read: Activity
/Fragment
god objects).
As a bridge from a "legacy" codebase, it makes sense to use it in this situation.
- But why would one do this? It hurts me to see application handed over to ViewModel. What would be an acceptable use case for this?
The use case for AndroidViewModel
is for accessing the Application
. In a "legacy" codebase, it's relatively safe to move "Context/Application-dependent code" out of Activities and Fragments without requiring a risky refactor. Accessing Application
in the view model will be necessary in that scenario.
- If there is any reason to use AndroidViewModel, can one not derive from ViewModel + use dagger2 for the application inject?
If you're not injecting anything else, then at best it's a convenient way to get an Application
reference without having to type cast or use any DI at all.
If you're injecting other members, be it with a DI framework or ViewModelFactory
, it's a matter of preference.
If you're injecting a ViewModel
directly into your Activity
/Fragment
, you're losing the benefits the platform is providing you with. You'll have to manually scope the lifecycle of your VM and manually clear your VM for your UI's lifecycle unless you also mess around with ViewModelStore
s or whatever other components are involved in retention. At that point, it's a view model by name only.
Android different ways to create viewModel object which one to use when?
In case anyone looking for in depth answer, please check this, here we have the following way to create or get the viewModel object:
val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)
val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)
val myViewModel4: MyViewModel by viewModels()
val myViewModel5 by viewModels<MyViewModel>()
All do the same thing, the only two key differences is:
- The viewModel initialisation with lazy loading and without lazy loading.
- The viewModel with multiple parameter and no parameters.
Lets see this wrt the lazy loading and without lazy loading
, the first three are without the delegate by
that means there is no lazy loading of that object, so it's the developer
responsibility to create the viewModel object only when activity is created or the fragment is attached to the activity, that means the first three approach(1, 2, 3) can't be
used at global scope, if used at global scope the variable must be
a var with lateint
or null initialisation, and the
initialisation(approach 1, 2, 3) must happen in the onCreate or
onViewCreated(in case of fragment).
Therefor the best way to create the viewModel object is using the delegate by
(4, 5), both are same with a bit different syntax, I choose 4 because of it's simplicity and readability.
val myViewModel4: MyViewModel by viewModels()
The by
delegate gives the flexibility to lazy load the instance and you can define the viewModel at global scope and get ride off the boilerplate code, if you try to initialise the viewModel at global scope without the delegate the app will crash since the viewModel will try to initialise before the activity is created(it will not lazy load the viewModel instance).
Now let's see how to lazy load with multiple parameters, the 6th
approach not mention in the question.
If you have multiple parameters in your view model and not using any dependency injection, you can use a ViewModelFactory implementation and then lazy load it:
val myViewModelWithParm: MyViewModel by viewModels { MyViewModelFactory(application, "param1", "param2") }
ViewModelFactory implementation:
class MyViewModelFactory(val application: Application, val param1: String, val param2: String) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MyViewModel(application, param1, param2) as T
}
}
Till this point we are clear on the delegate initialisation(4, 5), and how it is different with(1, 2, 3) now let's see the difference on the top 3 approach(1, 2, 3).
Let's first check 1 and 2.
val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)
The key difference in them is one uses ViewModelProvider.NewInstanceFactory
and other uses ViewModelProvider.AndroidViewModelFactory
, so I checked the source code of both the classes and found that ViewModelProvider.AndroidViewModelFactory
is actually the implementation of ViewModelProvider.NewInstanceFactory
which override the create
function that means both are doing the same stuff, preferable both approach should be chosen if we want multiple parameters however for that we have to override ViewModelProvider.NewInstanceFactory
to create our own factory like it's done here
Now comes the third one:
val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)
This is the simple form of 1 and 2 when we don't have multiple parameters in our ViewModel and don't want to lazy load the object.
Note: I highly recommend the approach 4 or 5(both are same with different syntax), since this is the most suitable and optimal to write, if you don't have multiple arguments, in case you have multiple arguments you can use the approach 6 mentioned in the answer by implementing ViewModelProvider.Factory
.
Related Topics
Drawable-Hdpi, Drawable-Mdpi, Drawable-Ldpi Android
Run Code Only Once After an Application Is Installed on Android Device
How to Get Retrofit Success Response Status Codes
How to Fix: "You Need to Use a Theme.Appcompat Theme (Or Descendant) with This Activity"
Listview in Arrayadapter Order Get's Mixed Up When Scrolling
Android Keyboard Hides Edittext
Disabling Android O Auto-Fill Service for an Application
How to Add an Image in Email Body
Android: How to Open Another App from My App
Handling Buttons Inside Android Notifications
How to Hide System Navigation Bar in Tablets
Android App Bundle with In-App Locale Change
Pass Arraylist<? Implements Parcelable> to Activity
How to Find the Logs on Android Studio
Check Whether Activity Is Active