In this article, I’ll show you how to inject ViewModel instances using Dagger 2 dependency injection framework. Turns out that’s not a simple task and there are multiple alternative approaches to choose from. In addition, I’ll discuss one potential tricky bug which you should avoid in your apps.
ViewModel Without External Dependencies
Let’s say that you use ViewModel to just store some data on configuration changes. In this case, you won’t need to pass any arguments into its constructor.
For example, consider this ViewModel:
public class MyViewModel extends ViewModel { private List<MyData> mData = new ArrayList<>(); public List<MyData> getData() { return mData; } public void setData(List<MyData> data) { mData = data; } }
To use MyViewModel in your Activities and Fragments, all you need to do is the following:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mMyViewModel = ViewModelProviders.of(this).get(MyViewModel.class); }
As you see, if your ViewModel doesn’t have external dependencies, then there is no reason to integrate it with Dagger at all. Simple.
Now, even though this scenario is simple, it’s still perfectly valid use case for ViewModel classes. If all you want to achieve is keeping some data on configuration changes, this approach will suffice.
ViewModel With External Dependencies
The official guidelines recommend using ViewModels to host the actual “presentation” (and, in some cases, even “business”) logic of your app, not just data. In these situations, the previous simple approach won’t do anymore.
For example, consider the following ViewModel. It uses FetchDataUseCase to fetch some data and then publishes the result using LiveData:
public class MyViewModel extends ViewModel { private final FetchDataUseCase mFetchDataUseCase; private final MutableLiveData<List<MyData>> mDataLive = new MutableLiveData<>(); private final FetchDataUseCase.Listener mUseCaseListener = new FetchDataUseCase.Listener() { @Override public void onFetchDataSucceeded(List<MyData> data) { mDataLive.postValue(data); } @Override public void onFetchDataFailed() { mDataLive.postValue(null); } }; @Inject public MyViewModel(FetchDataUseCase fetchDataUseCase) { mFetchDataUseCase = fetchDataUseCase; mFetchDataUseCase.registerListener(mUseCaseListener); } public LiveData<List<MyData>> getDataLive() { return mDataLive; } public void fetchData() { mFetchDataUseCase.fetchData(); } @Override protected void onCleared() { super.onCleared(); mFetchDataUseCase.unregisterListener(mUseCaseListener); } }
To instantiate this ViewModel, the system needs to pass FetchDataUseCase object into its constructor. If you just attempt to use the previously described approach, you’ll get a runtime error because Android wouldn’t know how to satisfy this constructor dependency. Therefore, we need a more sophisticated technique.
Enter ViewModelProvider.Factory
interface:
/** * Implementations of {@code Factory} interface are responsible to instantiate ViewModels. */ public interface Factory { /** * Creates a new instance of the given {@code Class}. * <p> * * @param modelClass a {@code Class} whose instance is requested * @param <T> The type parameter for the ViewModel. * @return a newly created ViewModel */ @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass); }
This interface realizes parameterized Abstract Factory design pattern. Its create(Class)
method receives a class object that represents the required ViewModel type as an argument and returns a new instance of the corresponding class.
To initialize ViewModels with non-default constructors, you’ll need to inject an object that implements ViewModelProvider.Factory
interface into your Activities and Fragments, and then pass it to ViewModelProviders.of()
method:
@Inject ViewModelFactory mViewModelFactory; private MyViewModel mMyViewModel; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mMyViewModel = ViewModelProviders.of(this, mViewModelFactory).get(MyViewModel.class); }
So far it doesn’t look too complex, but I haven’t shown you the implementation of ViewModelFactory yet. That’s where things get more involved.
Using Pre-Constructed ViewModel (Bad!)
You can implement ViewModelFactory in the following way:
public class ViewModelFactory implements ViewModelProvider.Factory { private final MyViewModel mMyViewModel; @Inject public ViewModelFactory(MyViewModel myViewModel) { mMyViewModel = myViewModel; } @SuppressWarnings("unchecked") @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { ViewModel viewModel; if (modelClass == MyViewModel.class) { viewModel = mMyViewModel; } else { throw new RuntimeException("unsupported view model class: " + modelClass); } return (T) viewModel; } }
This factory receives pre-constructed ViewModels as constructor arguments and then just returns them whenever clients ask for them. Unfortunately, if you’ll have 20 ViewModels in your application, then you’ll need to pass 20 arguments into ViewModelFactory’s constructor. It’s a bit ugly.
However, boilerplate isn’t the biggest problem with this implementation.
Note that to get an instance of such ViewModelFactory, all ViewModels that it provides will need to be instantiated as well. So, if you’ll have 50 ViewModels with 100 transitive dependencies and you’ll want to get an instance of ViewModelFactory, you’ll need to construct 150+ objects. Every single time. Not good.
But even that isn’t the biggest problem. The main problem with this implementation that makes it absolute no-go is the fact that it violates Liskov Substitution Principle and can lead to serious bugs in your application.
Consider this part of the contract of create(Class)
method in ViewModelProvider.Factory interface:
Creates a new instance of the given {@code Class}.
Can you see the problem?
The above implementation of ViewModelFactory doesn’t create a new instance. It will always return the same instance given the same argument. That’s bad.
For example, imagine that one day you’ll want to use the same ViewModel class in Activity and Fragment. Furthermore, you’ll want to get Activity’s ViewModel from within the Fragment. To achieve this functionality, you could add the following code in one of your Fragments:
@Inject ViewModelFactory mViewModelFactory; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mMyViewModel = ViewModelProviders.of(this, mViewModelFactory).get(MyViewModel.class); mMyViewModelActivity = ViewModelProviders.of(requireActivity(), mViewModelFactory).get(MyViewModel.class); }
Naturally, you’d expect these ViewModels to be different objects. However, given the above implementation of ViewModelFactory, it’s not guaranteed. Furthermore, the result of invocation of this code will depend on whether Activity’s ViewModel had been accessed in the past in another place in the application. This is very serious bug that can be extremely difficult to find and fix.
Therefore, you should never use this approach.
If you find it difficult to see why this implementation of ViewModelFactory is problematic, then it’s worth taking time to understand this point. I myself made a serious error initially and had’t realized the problem for months, so it’s alright if you find it tricky.
ViewModel Using Dagger Provider
The above implementation shouldn’t be used because it can lead to unintended reuse of ViewModel instances. Luckily, Dagger supports a handy convention which can easily resolve this problem: Providers
.
This implementation of ViewModelFactory is safe to use:
public class ViewModelFactory implements ViewModelProvider.Factory { private final Provider<MyViewModel> mMyViewModelProvider; @Inject public ViewModelFactory(Provider<MyViewModel> myViewModelProvider) { mMyViewModelProvider= myViewModelProvider; } @SuppressWarnings("unchecked") @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { ViewModel viewModel; if (modelClass == MyViewModel.class) { viewModel = mMyViewModelProvider.get(); } else { throw new RuntimeException("unsupported view model class: " + modelClass); } return (T) viewModel; } }
Now, instead of injecting ViewModels themselves into ViewModelFactory, I inject Providers of the respective ViewModels.
Providers are like proxies into Dagger’s internal construction logic for a single type. It means that whenever you “ask” Provider for an object, Dagger handles this request, taking the additional characteristics you might’ve specified for that type into account. For example, if MyViewModel in the above example would be defined as @Singleton scoped in application, then each call to mMyViewModelProvider.get()
would return the same instance. In other words, Providers are Dagger’s delegates that you can use inside your own classes.
The cool thing about Providers is that you don’t need to add them to objects graph manually. Dagger is smart enough to generate Provider<SomeClass>
for you if you have SomeClass
on the objects graph. Therefore, this change is conveniently limited to just ViewModelFactory.
ViewModel Using Dagger Multibinding
To address the issue of boilerplate in the above implementation, you can use special Dagger’s convention called “multibinding”. It’s relatively complex, so I’ll describe it in steps.
You start by defining a special annotation (I do it inside module class, but it’s not mandatory):
@Module public class ViewModelModule { @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @MapKey @interface ViewModelKey { Class<? extends ViewModel> value(); } }
ViewModelKey
annotation, when used on methods annotated with @Provides (“provider methods”), basically says that the services returned by these methods should be inserted into Map
. The keys in this Map
will be of type Class<? extends ViewModel>
and the values will be of type <? extends ViewModel>
(subclass of ViewModel
).
I’m pretty sure that this explanation sounds confusing at this point. Hopefully, it will become clearer after you see the entire picture.
Once I have ViewModelKey
annotation, I can add provider method for a specific ViewModel
in the following manner:
@Module public class ViewModelModule { @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @MapKey @interface ViewModelKey { Class<? extends ViewModel> value(); } @Provides @IntoMap @ViewModelKey(MyViewModel1.class) ViewModel viewModel1(FetchDataUseCase1 fetchDataUseCase1) { return new MyViewModel1(fetchDataUseCase1); } }
Note that the return type of the provider method is ViewModel
, not ViewModel1
. It’s intentional. @IntoMap
annotation says that Provider object for this service will be inserted into Map
, and @ViewModelKey
annotation specifies under which key it will reside. Still unclear, I know.
The net result of the above code will be that Dagger will create Map
data structure filled with Provider<ViewModel>
objects and then provide it implicitly to other services. You can make use of that Map
by passing it into ViewModelFactory
in the following manner:
@Module public class ViewModelModule { @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @MapKey @interface ViewModelKey { Class<? extends ViewModel> value(); } @Provides ViewModelFactory viewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> providerMap) { return new ViewModelFactory(providerMap); } @Provides @IntoMap @ViewModelKey(MyViewModel1.class) ViewModel viewModel1(FetchDataUseCase1 fetchDataUseCase1) { return new MyViewModel1(fetchDataUseCase1); } }
So, all the above “magic” was required to allow Dagger to create this Map
, fill it with ViewModel
s and then allow you to pass it into ViewModelFactory
. The implementation of the factory then becomes:
public class ViewModelFactory implements ViewModelProvider.Factory { private final Map<Class<? extends ViewModel>, Provider<ViewModel>> mProviderMap; @Inject public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> providerMap) { mProviderMap = providerMap; } @SuppressWarnings("unchecked") @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { return (T) mProviderMap.get(modelClass).get(); } }
As you can see, the factory becomes smaller (but not necessarily simpler). It retrieves the required Provider
object from Map
, calls its get()
method, casts the obtained reference to the required type and returns it. The nice thing about this approach is that this code is independent of the actual types of ViewModels in your application, so you don’t need to change anything here when you add new ViewModel classes.
Now, to add new ViewModel, you’ll simply add the respective provider method:
@Module public class ViewModelModule { @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @MapKey @interface ViewModelKey { Class<? extends ViewModel> value(); } @Provides ViewModelFactory viewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> providerMap) { return new ViewModelFactory(providerMap); } @Provides @IntoMap @ViewModelKey(MyViewModel1.class) ViewModel viewModel1(FetchDataUseCase1 fetchDataUseCase1) { return new MyViewModel1(fetchDataUseCase1); } @Provides @IntoMap @ViewModelKey(MyViewModel2.class) ViewModel viewModel2(FetchDataUseCase2 fetchDataUseCase2) { return new MyViewModel2(fetchDataUseCase2); } }
Dagger will automatically put Provider
for ViewModel2
into Map
and ViewModelFactory
will be able to use it.
The benefit of this method is clear: it reduces the amount of boilerplate that you need to write. However, there is also one major drawback: it increases the complexity of the code. Even experienced developers find this multibinding stuff tricky to understand and debug. For less experienced ones, it might be absolute hell.
Therefore, I wouldn’t recommend using this approach on projects with many developers, or if staff turnover rate is high. On such projects, it might require too much effort from all developers to learn Dagger’s multibinding. On small or personal projects, on the other hand, this approach might be alright.
Why ViewModel is So Complex to Use With Dagger
One very interesting question to ask in context of ViewModel is: why it requires special treatment, which is even more complicated than the “standard Dagger” (which isn’t simple to begin with)?
Well, consider this code that you’d use in either Activity or Fragment:
@Inject ViewModelFactory mViewModelFactory; private MyViewModel mMyViewModel; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mMyViewModel = ViewModelProviders.of(this, mViewModelFactory).get(MyViewModel.class); }
Note that what you need in this Activity or Fragment is MyViewModel, but what’s being injected is ViewModelFactory.
Factories are very useful constructs when you need to instantiate objects with runtime dependencies, or when you need to instantiate many objects at runtime, or when the specific type of object that you’ll need isn’t known at compile time. Nothing of that applies in this case, however. You need one instance of ViewModel and you know its type and its dependencies at compile time. Therefore, usage of abstract factory pattern is unjustified in this case.
Unnecessary dependencies like ViewModelFactory violate fundamental principle of object-oriented design called the Law of Demeter. It’s the violation of LoD that causes the troubles with respect to dependency injection. Just think about it for a moment: Dagger already knows how to provide your ViewModels, but, instead of simply injecting them directly, you are forced to use ViewModelFactory. It’s a shame. In many cases you can refactor your code to fix LoD violations, but, in this case, it’s violated by ViewModel framework itself, so you can’t work around that.
Bottom line is that it’s important to know the rules of object-oriented design and apply them in practice. Especially if you design APIs for millions of other developers to use.
ViewModel with SavedStateHandle
If you’ll need to preserve ViewModel’s state on process death, you’ll need to use so-called Saved State module for ViewModel. This approach basically amounts to injection of SavedStateHandle
data structure into your ViewModel and then using it to hold ViewModel’s state.
The resulting ViewModel might look like this (note the change in the constructor):
public class MyViewModel extends ViewModel { private final FetchDataUseCase mFetchDataUseCase; private final MutableLiveData<List<MyData>> mDataLive; private final FetchDataUseCase.Listener mUseCaseListener = new FetchDataUseCase.Listener() { @Override public void onFetchDataSucceeded(List<MyData> data) { mDataLive.postValue(data); } @Override public void onFetchDataFailed() { mDataLive.postValue(null); } }; @Inject public MyViewModel(FetchDataUseCase fetchDataUseCase, SavedStateHandle savedStateHandle) { mFetchDataUseCase = fetchDataUseCase; mDataLive = savedStateHandle.getLiveData("data"); mFetchDataUseCase.registerListener(mUseCaseListener); } public LiveData<List<MyData>> getDataLive() { return mDataLive; } public void fetchData() { mFetchDataUseCase.fetchData(); } @Override protected void onCleared() { super.onCleared(); mFetchDataUseCase.unregisterListener(mUseCaseListener); } }
But where SavedStateHandle comes from? There is a new type of factory which provides it: SavedStateViewModelFactory
. If your ViewModel doesn’t have additional dependencies, then you can use that factory as is. However, the above ViewModel requires FetchDataUseCase, so the default factory won’t to.
To satisfy custom ViewModel’s dependencies, you’ll need your ViewModelFactory to extend AbstractSavedStateViewModelFactory
class:
public class ViewModelFactory extends AbstractSavedStateViewModelFactory { private final Provider<FetchDataUseCase> mFetchDataUseCaseProvider; @Inject public ViewModelFactory( Provider<FetchDataUseCase> fetchDataUseCaseProvider, SavedStateRegistryOwner owner, @Nullable Bundle defaultArgs ) { super(owner, defaultArgs); mFetchDataUseCaseProvider = fetchDataUseCaseProvider; } @Override protected <T extends ViewModel> T create(String key, Class<T> modelClass, SavedStateHandle handle) { ViewModel viewModel; if (modelClass == MyViewModel.class) { viewModel = new MyViewModel(mFetchDataUseCaseProvider.get(), handle); } else { throw new RuntimeException("unsupported view model class: " + modelClass); } return (T) viewModel; } }
Note the change of constructor’s signature. SavedStateRegistryOwner
is a new interface implemented by Activities and Fragments. In addition, note the change in the signature of create()
method. It’s through this new method that you get SavedStateHandle which then needs to be passed into ViewModels.
Dependency Injection with Dagger and Hilt Course
Learn Dependency Injection in Android and master Dagger and Hilt dependency injection frameworks.
Go to CourseAn unfortunate consequence of this implementation is that as long as you pass SavedStateHandle into ViewModel’s constructor, you can use neither multibinding, nor Providers for ViewModels inside ViewModelFactory anymore. Instead, you’ll need to pass providers for all the transitive dependencies into the factory and construct the ViewModel there manually. Of course, that’s additional work, especially if you’ll have many complex ViewModels in your app, but that’s what it is.
There is an additional library called AssistedInject which you can use to remove part of the additional boilerplate when using SavedStateHandle, but I can’t recommend it. In my opinion, AsisstedInject results in too much complexity in the source code.
Conclusion
In this post I demonstrated how you can integrate ViewModel framework with Dagger. One of the discussed approaches violates Liskov Substitution Principle, and, as such, should never be used.
To be honest, when it comes to ViewModel, there is no “clean” way of doing things. Therefore, you need to take into account your specific constraints and make an educated trade-off. In essence, you need to choose the lesser of the evils.
To make it absolutely clear for long-term readers of my blog: the fact that I write about ViewModels isn’t an endoresement. My opinion about this framework hasn’t changed (if anything, it became even more negative). I don’t use ViewModels in my projects, and I don’t recommend you using them either. The only reason I wrote this post is to help developers who search for this information.
[Update 2020: if you want to master Dagger 2 and Hilt (which is the next-generation DI framework from Google), check out my comprehensive course about dependency injection in Android. It covers everything professional Android developers need to know about Dagger 2.]
As usual, thanks for reading and you can leave your comments and questions below.
All of this presents itself to me as a strong argument to throw Java in the trash can, forget that you ever had to deal with Dagger’s nonsense, and move to Koin with Kotlin. It’s really ridiculous.
Hello George,
As I explained in the article, IMHO ViewModel’s API was poorly designed, which gives rise to all these complications. No language or framework will “hide” that fact.
If anything, Dagger’s multi-bindings opens some options which means that developers can choose their poison at least.
Therefore, I don’t think that either Kotlin or Koin have any kind of simplification to offer. Or, maybe, I’m missing something?
Hi Vasiliy, I disagree that ViewModel library is poorly designed for the reason you stated of violating the Law of Demeter. The reason for using a factory is because library decides when a new instance is required — it’s a side-effect of the overly complex Android lifecycle. You could argue that ViewModels should not be retained across configuration changes, but that decision put a limit on performance for many apps since they would have to re-query data after every configuration change.
Congratulations Vasiliy. Very good explanation. It has worked. I’d like to share the approach below which avoids boilerplate code with Dagger 2. I joined your view model injection approach with the one below.
https://proandroiddev.com/using-dagger-android-in-a-multi-module-project-e751afaa53ba
Unfortunately, the real problem is still present even with these improvements.
The code has a pretty serious ambiguity: you can scope your factory and dependencies using dagger scopes, but in the end, the factory create method will only be called when there is no instance of VM cached in the ViewModelStore. If there is an instanced cached, it will just return it, containing references to dependencies that differ from the ones in the component created at onCreate.
Any thought on that?
Hello Matheus,
I don’t see a problem with that, as long as ViewModel doesn’t have a direct or transitive reference to Activity. However, this is a general limitation of ViewModel framework which can’t be worked around. It’s one of the reasons I don’t use ViewModel myself.
Can you think of any specific scenario where what you described might become an issue?
Thanks Vasiliy for this amazing step by step explanation. However, don’t you think if we add multiple view models in below module, It’ll initialise all view models at once to create map for Factory ?
[Vasiliy: I removed the code because it was identical to the example with multi-bindings and was a bit too long for a comment]
Hello Ashutosh,, Provider> providerMap>. It doesn’t contain ViewModels, but Provider objects. Instantiating Providers is cheap, therefore it’s not a problem. ViewModel themselves will be instantiated only when you call get() method of the respective Provider, in other words – each ViewModels will be instantiated only when it’s needed.
I edited your question a bit and removed the code. You basically ask whether in multi-binding example all ViewModels will be instantiated right away to create the Map, right?
Note that the map is of type Map
Cheers
Nice explanation , dagger 2 really powerfull, thanks you so much !!
Thanks, Vasiliy for this great article.
While I have been making some deep research to understand the issue better, I encountered some interesting situations and wanted to mention them.
Found an article and it claims that your approach is not valid anymore because the SavedStateViewModelFactory is now the default ViewModel factory and a unique savedstatehandle in each ViewModel constructor is a must, this way of injecting ViewModels might not be valid anymore. https://www.coroutinedispatcher.com/2019/08/how-to-produce-savedstatehandle-in-your.html
Also, I found this repo and it looks like the author of the repo managed to inject the ViewModel directly in the Activity/Fragment.
https://github.com/alexmamo/FirebaseApp-Clean-Architecture-MVVM/blob/master/app/src/main/java/ro/alexmamo/firebaseapp/auth/AuthActivity.java
As I do think that you have a very good understanding of the topic, I wanted to ask your opinions regarding the issues mentioned above.
Cheers
Hi Ogün,
Maybe I’ll explore SavedState in one of my future posts.
However, given that ViewModel-SavedState hit 1.0.0 milestone less than two weeks ago (long after that post you referenced had been written), and given that many projects had already used ViewModels before that happened, I don’t see how this approach can be “not valid anymore”. There might be an additional approach now, but backward compatibility requires that the “old” approach would keep working, unless you explicitly migrate to newer versions.
As for that project where ViewModel is injected directly, I’m not going to review it, but it looks very suspicious.
Hi Vasily.
Very nice step by step explanation, thank you for the article. However, I have some questions.
Every ViewModel has a dependency on a use-case and use-cases also have theirs on dependencies. I think they should be scoped by feature (e.g. a new Dagger 2 Component for the feature with its own scope, created in the related Activity/Fragment, depends on the singleton scoped AppComponent). ViewModel cannot be annotated because of its nature. So its dependencies and sub dependencies also cannot be annotated with the feature scope as the feature Component depends on the AppComponent. They can be all annotated as Singleton but this is a problem in my eyes. Because making them all singletons and keeping them for the entire lifecycle of the app might cause problems as the app grows.
So, my question is, how to manage this scope issue? How should the ViewModel dependencies and sub-dependencies be scoped?
Hello Ogün,
I actually never do the kind of scoping that you described. When I set Dagger up, there are just two or three scopes: ApplicationScope, unscoped and, in some cases, ActivityScope. Absolute majority of the services should be unscoped, with some global objects living in ApplicationScope and very few special objects living in ActivityScope.
You can read a bit more elaborated discussion in my answer to this SO question.
Vasiliy
Hi, Visily!
Thank you so much for this post, great work.
I started to integrate Dagger into a project, but to my surprise I was able to inject the ViewModels without a ViewModelProvider.Factory, I just annotated every one of my ViewModels with @Inject (like every other class) and they worked. The app is running perfectly (accordgin to me). I’d like to know why, because every post about this topic recommends you to use ViewModelProvider.Factory.
Was I able to do it because a new update in wich is now allowed to do it that way?
Is it running properly or is it just running but with some flaws I don’t know about?
I don’t know, maybe it is running but under the hood something is not working as it should.
I’d like to know your thoughts about this before executing a commit to the project.
Thanks!
Hello Armando,
You can surely inject ViewModels directly, but then, they will not be preserved on config changes. Therefore, if you rotate the device, go into multi-window mode, change locale, etc., your ViewModels will be re-created. That’s not how ViewModels were meant to be used.
That Factory that you bypassed is needed to make ViewModel survive configuration changes.
Regards
That’s what I needed to know, thank you very mucho for your answer, I already did it following your tutorial, great work, keep it up.
Hi Vasily,
Very nice article, as usual. I’m using this technique to inject Viewmodels in a project . How can I use Shared Viewmodels with fragments and activity using this approach? It’s my understanding that they will need to be singleton right?
I have another doubt, this technique prevents the recreation of Viewmodel on configuration changes but also when the app goes to background?
Thnx again!
Hi Ernest,
To get a shared ViewModel you don’t need to change anything in DI setup. Just pass a reference to Activity as the “owner” of the requested ViewModel. As long as all Fragments will pass the same reference and specify the same type of VM, they’ll get the same instance.
As for “app goes to background”, it depends on what happens when the app is backgrounded. If the app isn’t killed while it’s in background, then VM is retained. But if it is killed, then you’ll get a new instance of VM when you bring the app back to screen.
Hi Vasily,
I have a question.. Why don’t inject UseCases instances (or any dependency of ViewModel) into ViewModelFactory, and then pass that instances into ViewModel’s constructor? Then you have a ViewModelFactory field annotated with @Inject in Fragment, a constructor annotated with @Inject in ViewModelFactory with UseCases parameters, and a ViewModel with a constructor with UseCases parameters and no annotation.
Thanks
Hello Jorge,
This approach will have similar drawbacks and limitations as injecting pre-constructed ViewModels into ViewModelFactory. Specifically: many constructor arguments, each of which will be constructed each time you need a new instance of ViewModelFactory. Furthermore, all these dependencies will be reused within the scope of a single Factory, which can lead to bugs. You could resolve some of these issues by wrapping dependencies into Providers, but, at that point, I believe that the implementation you propose will already be more complex than the one you saw in this article.
How to preserve compile time safety for the VMs with this approach?
Dima,
If you use multibinding, you lose compile time safety and I don’t think there is a way around that.