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.

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.

As usual, thanks for reading and you can leave your comments and questions below.