How to Inject ViewModel using Dagger 2

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 ViewModels 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 Course

An 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.

Check out my premium

Android Development Courses

22 comments on "How to Inject ViewModel using Dagger 2"

  1. 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.

    Reply
    • 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?

      Reply
      • 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.

        Reply
  2. 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?

    Reply
    • 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?

      Reply
  3. 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]

    Reply
    • Hello Ashutosh,
      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, 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.
      Cheers

      Reply
  4. 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

    Reply
    • 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.

      Reply
  5. 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?

    Reply
    • 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

      Reply
  6. 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!

    Reply
    • 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

      Reply
      • 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.

        Reply
  7. 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!

    Reply
    • 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.

      Reply
  8. 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

    Reply
    • 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.

      Reply

Leave a Comment