Flatsome Theme Looks Like Somethingã¢â‚¬â„¢s Gone Wrong. Wait a Couple Seconds
How To Inject ViewModel With Dagger & What Might Go Wrong
Handling ViewModel Dependency Injection is a popular topic with manuals all over the internet, only permit's cheque them for hidden traps and disadvantages.
The main problem is that ViewModel should be created using "ViewModelProvider(this, factory).get(YourViewModel::class.java)" at some betoken. It might be subconscious inside a delegate "by viewModels { factory }" or be called directly. However, without information technology, ViewModel won't persist through configuration changes, and its onCleared method won't be chosen when information technology'due south no longer needed.
Notation: This commodity requires some knowledge of Dagger.
To make our examples as simple as possible, I'll assume that nosotros use a single component, AppComponent. Almost examples could scale to architecture with a Subcomponent per view model or to a single Subcomponent for all view models.
In basic scenarios our VM volition look similar this:
The Repository is provided past some module in AppComponent or just has a constructor annotated with @Inject.
As well, I assume that we tin can easily become AppComponent from fragments with a method like:
Now allow'due south go over our possible approaches and their disadvantages.
ane. Providers map in ViewModelProvider.Factory (with or without multibindings)
There are several methods. The easiest way is to inject view model providers into a mill and map them manually:
Add together this manufacturing plant to the component (AppComponent is used for simplicity):
Y'all tin can then hands create your ViewModel in fragment or activity similar so:
This approach has different variations with multibindings, but the core thought is the aforementioned.
Although it works well and allows us to create a VM in a few lines, it has certain limitations:
- Information technology makes our factory a service locator, which means that if yous forget to add mapping in the factory (or in a module if you use multibinding), then your app volition crash in runtime without any indication that something was incorrect during compilation. Compile-time safety is a great advantage of Dagger and we don't desire to lose information technology.
- Information technology doesn't allow us to pass any arguments to VM that aren't available from DI. Nosotros tin can't use assisted injection with this approach, though if all view models share the same list of parameters it would be possible, e.thousand., for SavedStateHandle.
2. Use Hilt
Hilt is a great new tool for Android developers that greatly simplifies some aspects of working with Dagger (and makes the learning curve even steeper). While you don't want to pass anything from Fragment to view model information technology works like a charm:
And in Fragment you need only one line:
Of course for all this to work yous need to set Hilt correctly, which is covered by multiple articles and an official guide. Notice that if you forget to add @HiltViewModel, and then your app volition crash in runtime, not in compilation time.
But if we wanted to pass something from Fragment to view model, we would have to inject an assisted Manufactory into a Fragment and so create a ViewModel Factory that uses it.
In this case our view model would look like this:
Nosotros would also need some kind of universal VM mill simply to avoid average factories for each VM:
Now, nosotros tin inject the Factory into a Fragment and utilise information technology for creating a VM like this:
This way we lose some advantages of Hilt and it works much like Dagger with extra steps. If you're OK with that or don't need an assisted injection for VM, then Hilt is a neat choice.
three. Get VM from DI and pass the reference to Factory in viewModels delegate
This one won't work, considering the lambda that we passed to viewModels is invoked after configuration changes. This is rather unusual, only there are cases of a more complicated form of this solution.
viewModelComponent().myViewModel() being called subsequently every configuration change will atomic number 82 to ViewModels multiplying. If yous use some resource inside or launch some coroutines in an init {} block, and then those resource and coroutine context won't be closed or cleared.
Even if this approach worked, there'south still a possibility that someone volition utilize information technology with ViewModelProvider().get() directly and feel the same problems.
4. Pass lambda for creating VM to Factory
This one works fine. Only there'south a hidden danger to information technology: Imagine that y'all used ViewModel to persist some utility class through orientation change. Permit'south call it Router, for example. The fundamental hither is that it has a constructor marked with an @Inject annotation and extends ViewModel.
Later you tin add together this Router into your VM'south constructor without noticing that the Router extends ViewModel.
What volition happen then? The Router will be created alongside your VM and injected into its constructor, just when onCleared() is chosen for VM, the same method won't be called for the Router. It might potentially pb to memory leaks and bugs that are quite hard to catch.
Note that the aforementioned might happen with injecting more than obvious view models into your VM. It would be incorrect to use VM this way, but the best approach doesn't leave space for error. Otherwise, nosotros'd use Koin or another service locator instead of Dagger.
So how should we avoid this potential problem? We could utilise @AssistedInject.
five. Utilise @AssistedInject
If we hold to apply @AssistedInject with all classes extending ViewModel, we wouldn't take the problem from the case higher up, and we'd too be able to inject boosted parameters into our VM.
Let's change our ViewModel to work with assisted injection:
Prepare a universal Factory similar to the previous example:
Add a single method for hands creating a VM consul:
Alter AppComponent:
And we tin finally create our view model:
This solution is a bit more than complicated than the previous 1, but it allows usa to avoid a rare and complicated trouble that would be quite difficult to debug, and likewise provides usa with a fashion to laissez passer some extra data to ViewModel's constructor, including SavedStateHandle, screen params, and more.
half-dozen. A bonus
In my previous article, I proposed an approach that liberates our view models from extending the ViewModel class and also introduces an culling way to handle cleaning resource in view models. A similar approach is used in this library, so I'chiliad not the only one who's using it.
If yous'd read that article then you may notice that lazyViewModel looks similar to method getOrCreatePersisted from that article, though the latest doesn't return a delegate.
We can pack all common dependencies from that article into a single subcomponent like that:
Create an extension function for creating this component in fragment:
Add dependency and remove ViewModel superclass from our VM:
Pack lazy and getOrCreatePersisted in 1 method for simplicity:
And now nosotros tin can create view models with ease:
This way we don't accept to apply assisted injection if we don't demand to pass actress parameters to the view model, and all dependencies of our view model that crave cleaning resource will be able to do so by accepting PersistentLifecycle as a constructor parameter. Also, our view model layer won't depend on the Android framework (though it still depends on Dagger, so no kotlin multiplatform yet).
Conclusion
Although it's not e'er piece of cake to detect, there are means to inject parameters into ViewModel with Dagger without losing its compile-fourth dimension dependency graph verification and a boilerplate code. Especially if you don't mind getting rid of ViewModel superclass, always creating VM with assisted injection, or keeping an eye for potential nested VM injections.
Source: https://medium.com/wriketechclub/how-to-inject-viewmodel-with-dagger-what-might-go-wrong-7954372a7fb9
0 Response to "Flatsome Theme Looks Like Somethingã¢â‚¬â„¢s Gone Wrong. Wait a Couple Seconds"
Enregistrer un commentaire