I researched a lot but I didn't understand the basic difference between the following topics. Please, tell me what is the basic difference between them and when to use them. And if possible please provide article links other than the Android developers guide. Thanks a lot!
ViewModel
AndroidViewModel
ViewModelProvider.Factory
ViewModelProvider.NewInstanceFactory
ViewModelProvider.AndroidViewModelFactory
It's easier to start by explaining the factories.
ViewModelProvider.Factory is necessary because the framework needs a way to be able to create instances of your ViewModel on your behalf, because it has to do this when returning from process death. You don't create instances directly yourself except inside the Factory.
The framework automatically provides some default factories that are capable of creating instances of your ViewModel if your ViewModel constructor's arguments are one of the following:
constructor() Empty constructor (no arguments)
constructor(savedStateHandle: SavedStateHandle)
constructor(application: Application)
constructor(application: Application, savedStateHandle: SavedStateHandle)
So if your ViewModel constructor is like one of the above, you don't have to create your own ViewModelProvider.Factory. In your Fragment or Activity, you can simply use private val viewModel: MyViewModel by viewModels() to create it using the default factories.
You don't need to touch or even think about NewInstanceFactory or AndroidViewModelFactory. They are subtypes of Factory that the framework uses as the default implementations that can construct the above types of ViewModels.
AndroidViewModel is a ViewModel with an Application property. If your ViewModel constructor is like one of the last two in the list above and you want to avoid creating your own factory, you must subclass AndroidViewModel instead of ViewModel. The default factories for some reason will only handle those last two if your factory is an instance of AndroidViewModel. (Seems like an unnecessary and pointless restriction to me, but maybe I'm missing something.)
Related
In this link it is instructed to use viewModel() in any composable and in an activity, we will get the same object while calling viewModel(). Although it is instructed to use viewModel() inside a composable, I was able to use it in setContent{} (outside of any composable) also.
In this link it is instructed to use viewModels() in an activity or a fragment to get the object of a class that extends ViewModel.
In both of the cases, we are getting an object of a class that extends ViewModel. So, why do we need to use two different approaches (viewModel() and viewModels())?
If you ask if you can use only viewModel without viewModels in Compose, the answer is yes. But in some cases it is more convenient to use both of them.
viewModels belongs to the androidx.activity package, is an extension to ComponentActivity, and has nothing to do with Compose. It was used in view-based Android and can still be used with Compose when you need to initialize or update your view model with some activity-specific callbacks.
In turn, viewModel is part of Compose and allows you to easily create/access a view model from any Composable.
You can call it directly inside setContent since it already belongs to the composable scope, but you would not be as comfortable calling it anywhere else in the activity, such as in onActivityResult(I know it's deprecated, it's just an example). You can still do it as shown in this answer, but in some cases viewModels may be easier to use.
in my app I have a MainActivity which requires access to a ViewModel. I am injecting the ViewModel using DaggerHilt and the #ViewModelInject annotation. Additionally, I have two Fragments within the Activity that require access to the same ViewModel in order to pass data to each other using observables.
The problem:
I have found that whenever one of my Fragments go through onDestroy() its ViewModel is killed. This leads me to think that the Activity and Fragments are not sharing the same ViewModel.
My question:
Does anyone know if we are supposed to use scope annotations for ViewModels in Dagger Hilt? I didn't see this stated in the Hilt docs or the android dev tutorials/guides. I had assumed that they were making ViewModels app level singletons, which would make sense.
If we do have to use scope annotations for ViewModels, does anyone know which level is appropriate?
This is my viewmodel code:
class MainActivityViewModel #ViewModelInject constructor(
private val repo: Repo,
private val rxSharedPrefsRepo: RxSharedPrefsRepo,
private val resourcesRepo: ResourcesRepo,
#Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
As per the Scoping in Android and Hilt blog post, using #ViewModelInject means that the objects you pass into the ViewModel are scoped to the ViewModel.
The scope of the ViewModel, however, is based on how you get the ViewModel (what ViewModelStore the ViewModel is associated with) - not anything that Hilt controls. If you use by viewModels() in a Fragment, then the ViewModel is scoped to the Fragment. If you use by activityViewModels() or by navGraphViewModels(), then the ViewModel would be scoped to the activity or navigation graph, respectively.
As mentioned in the blog post, if you want an object that is scoped to the activity and survives configuration changes, you can use Hilt's #ActivityRetainedScoped on any object and inject that object into both fragments.
Whether you should use #ActivityRetainedScoped or a ViewModel where you control the scope separately from Hilt is covered in the blog post:
The advantage of scoping with Hilt is that scoped types are available in the Hilt component hierarchy whereas with ViewModel, you have to manually access the scoped types from the ViewModel.
The advantage of scoping with ViewModel is that you can have ViewModels for any LifecycleOwner objects in your application. For example, if you use the Jetpack Navigation library, you can have a ViewModel attached to your NavGraph.
Hilt provides a limited number of scopes. You might find that you don’t have a scope for your particular use case — for example, when using nested fragments. For that case, you can fall back to scoping using ViewModel.
Inside my fragment class, when I am getting my viewModel, I can write my code in two different ways.
Using "viewModelStore"
ViewModelProvider(viewModelStore, viewModelFactory).get(FragmentViewModel::class.java)
Using "this"
ViewModelProvider(this, viewModelFactory).get(FragmentViewModel::class.java)
My question is, does any difference exist between the two alternatives, and if yes which one is the preferable approach?
If you're providing your own ViewModelProvider.Factory, then there's no difference, so just use what is easier, this.
Of course, if you're in Kotlin, you don't need to use ViewModelProvider directly at all, you'd instead want to use Fragment KTX and use
val viewModel: FragmentViewModel by viewModels { viewModelFactory }
Note that if you were not using your own factory, you should always pass in a ViewModelStoreOwner (i.e., this) instead of just passing in the ViewModelStore since the Javadoc explicitly mentions:
This method will use the default factory if the owner implements HasDefaultViewModelProviderFactory. Otherwise, a ViewModelProvider.NewInstanceFactory will be used.
The ViewModelStore constructor is not able to get the correct default factory.
Why should I use viewmodelproviders for viewmodels?
Why I just can't add custom singleton annotation to my viewmodel, and then inject this viewmodel to fragment class?
Like so:
#MainScope
class MainViewModel #Inject constructor(): ViewModel()
And then:
open class BaseFragment<T: ViewModel>: DaggerFragment() {
#Inject
protected lateinit var viewModel: T
Both cases are independent of screen rotation.
Is there any drawbacks of singleton annotation case?
I see only advantages, with this approach I don't need to copy/paste tons of code.
Why should I use viewmodelproviders for viewmodels?
To get viewModel.onCleared() callback called properly at the right time by the ComponentActivity.
(and to ensure it's created only once for the given ViewModelStoreOwner).
Why I just can't add custom singleton annotation to my viewmodel, and then inject this viewmodel to fragment class?
Because you won't get viewModel.onCleared() callback called properly at the right time by the ComponentActivity.
Is there any drawbacks of singleton annotation case? I see only advantages,
That you don't get viewModel.onCleared().
Also if you have a singleton variant, then the ViewModel won't die along with its enclosing finishing Activity, and stay alive even on back navigation (which is probably not intended).
with this approach I don't need to copy/paste tons of code.
You're using Kotlin. Use extension functions.
One of the most up to date samples covering Android Architecture Components is GithubBrowserSample provided by Google. I reviewed the code and a few questions arose:
I have noticed that ViewModelModule is included in AppModule. It means that all the viewmodels are added to the DI graph. Why that is done in that way instead of having separate Module for each Activity/Fragment that would provide only needed ViewModel for specific Activity/Fragment?
In this specific example, where viewmodels are instantiated using GithubViewModelFactory is there any way to pass a parameter to the specific ViewModel? Or the better solution would be to create a setter in ViewModel and set needed param via setter?
[...] It means that all the viewmodels are added to the DI graph. Why that is done in that way instead of having separate Module for each Activity/Fragment [...]?
They are added to the DI graph, but they are not yet created. Instead they end up in a map of providers, as seen in the ViewModelFacory.
#Inject
public GithubViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) { }
So we now have a GithubViewModelFactory that has a list of providers and can create any ViewModel that was bound. Fragments and Activities can now just inject the factory and retrieve their ViewModel.
#Inject
ViewModelProvider.Factory viewModelFactory;
// ...later...
repoViewModel = ViewModelProviders.of(this, viewModelFactory).get(RepoViewModel.class);
As to the why...alternatively you could create a ViewModelProvider.Factory for every Activity / Fragment and register the implementation in every Module. This would be a lot of duplicated boilerplate code, though.
In this specific example, where viewmodels are instantiated using GithubViewModelFactory is there any way to pass a parameter to the specific ViewModel? Or the better solution would be to create a setter in ViewModel and set needed param via setter?
It seems like all the ViewModels only depend on #Singleton objects—which is necessary, since they all get provided from the AppComponent. This means that there is no way to pass in "parameters" other than other #Singleton dependencies.
So, as you suggested, you'd either have to move the factory down into the Activity / Fragment component so that you can provide lower-scoped dependencies, or use a setter method.