Open In App

Assisted Dependency Injection in ViewModel with Dagger-Hilt in Android

Last Updated : 17 Mar, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

If you use Dagger-Hilt Dependency Injection in your android project then you must have encountered a case where you want to manually supply some parameters to construct an object in order to inject it. Generally when we inject a dependency/object in a class then the Dagger-Hilt framework construct it behind the scenes by automatically passing specified parameters to it but what when dagger-hilt doesn’t know about some dependencies that how to inject them & we want to manually supply those dependencies at runtime by our own. 

Example: When we create a ViewModel object then in viewmodel’s constructor we specified required dependencies (it might require your network repository object & your local database/DAO object, etc) to be injected by dagger-hilt in order to construct it. Now let’s assume while dagger-hilt is injecting required specified dependencies in viewmodel, you also want to manually pass some object/dependency at runtime in viewmodel to construct it… So, How would you do that ?

So, in this article, we will see how we can manually assist some dependencies at runtime along with dagger-hilt supplied dependencies. If you want to see how to set up/use Dagger-Hilt dependency injection then please first refer to the above tagged Dagger-Hilt article.

Understand How to use Assisted Injection in case of ViewModel with an Example

Suppose we have ArticlesFeedViewModel which is to be injected in ArticlesFeedFragment. Now suppose as this fragment will appear on the screen you need to fetch the list of all the published articles by you & have to show them into UI and to make that api call, we are calling getPublishedArticles() function present in ArticlesFeedViewModel’s init{ } block so that api gets called as soon as this viewmodel gets initialized. Now ArticlesFeedViewModel’s getPublishedArticles() will further call a function present in ArticlesRepository to start making a network call, So we need a ArticlesRepository object too in this viewmodel & we are injecting ArticlesRepository in viewmodel’s constructor with the help of Dagger-Hilt.

But have you noticed that we are also having one more object/dependency named userId annotated with @Assisted? Actually we need to pass that userId at runtime in getPublishedArticles() function in order to fetch articles published by you & we are getting this userId in ArticleViewModel from ArticlesFeedFragment as soon as viewmodel gets instantiated in that fragment. But how ? we will see this in while, let’s first better understand the code inside viewmodel.

Understanding ViewModel’s code

Compare to normal injection, We need to make a few changes in the case of Assisted Injection:

  • We use @AssistedInject instead of @Inject for constructor injection.
  • Arguments which need to be provided at runtime are annotated with @Assisted.

Now, after doing the above changes still we can’t directly inject our viewmodel in fragment/activity, we first need to create a factory for it because we will use that factory to create an instance of the actual class i.e. viewmodel in our case. (please note that we create a factory for our class/viewmodel as an interface)

So, let’s talk about factory now:

  • We annotate our factory with @AssistedFactory. This annotation tells the Dagger-Hilt that this interface is used to create an instance of a class/viewmodel that requires Assisted Injection.
  • Inside this factory, we create a function named “create”that will be responsible for returning an instance of our class i.e. ArticlesViewModel and accepts only those arguments which are to be provided/assisted by us at runtime (i.e. userId) that means arguments annotated as @Assisted.

Now we will create a factory provider function named providesFactory() which will provide ViewModelProvider.Factory and we will have anonymous class inside it that will again have overridden create a function which we have implemented to actually create an instance of our viewmodel. Note that providesFactory() function will require assistedFactory & all the assisted arguments to be supplied at runtime.

Kotlin




// We are using @AssistedInject instead
// of normal @Inject as we want
// to assist one dependency by our own
// at runtime and rest by dagger-hilt
class ArticlesFeedViewModel @AssistedInject constructor(
    private val articlesRepo: ArticlesRepository,
    // dependency which is to be assisted by
    // us should be annotated with @Assisted
    @Assisted
    private val userId: String
) : ViewModel {
 
    // It's a factory of this viewmodel, we need
    // to annotate this factory interface
    // with @AssistedFactory in order to
    // let Dagger-Hilt know about it
    @AssistedFactory
    interface ArticlesFeedViewModelFactory {
        fun create(userId: String): ArticlesFeedViewModel
    }
 
    // Suppressing unchecked cast warning
    @Suppress("UNCHECKED_CAST")
    companion object {
       
        // putting this function inside
        // companion object so that we can 
        // access it from outside i.e. from fragment/activity
        fun providesFactory(
            assistedFactory: ArticlesFeedViewModelFactory,
            userId: String
        ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                 
                // using our ArticlesFeedViewModelFactory's create function
                // to actually create our viewmodel instance
                return assistedFactory.create(userId) as T
            }
        }
    }
 
    init {
       // Calling getPublishedArticles() function
       // in viewmodel's init block
       // because init block just gets executed
       // after primary constructor
       // So, actually this api will get called as soon
       // as viewmodel gets instantiated
       getPublishedArticles(userId)
    }
 
    private fun getPublishedArticles(userId: String) {
        // Just for sack of example we are just
        // simply storing response in a variable
        val response = articlesRepo.getPublishedArticles(userId).cachedIn(viewModelScope)
    }


 
 

Injecting Viewmodel’s Factory & viewmodel itself

 

We are using Jetpack Navigation Component in this example for fragments navigation. Now let’s assume we are navigating to ArticlesFeedFragment from let’s say HomeFragment & we are sending userId from HomeFragment to ArticlesFeedFragment by using Safe Args.

 

So, first we have injected our ArticlesViewModel’s factory. Now we will instantiate our viewmodel with the help of kotlin delegate property “by ” and then we’ll call ArticlesFeedViewModel.providesFactory() fun inside it that will require assisted factory i.e. our above-injected ArticlesFeedViewModelFactory and manually assisted userId which we are getting with help of safe args.

 

Kotlin




@AndroidEntryPoint
class ArticlesFeedFragment : Fragment() {
 
    // using viewbinding
    private var _binding: FragmentArticlesFeedBinding? = null
    private val binding get() = _binding!!
     
    // Using navargs for getting argument
    // supplied from previous fragment
    private val args: ArticlesFeedFragmentArgs by navArgs()
 
    // First injecting our
    // viewmodel's factory interface
    @Inject
    lateinit var articlesFeedViewModelFactory: ArticlesFeedViewModel.ArticlesFeedViewModelFactory
 
    // Creating viewmodel here with
    // the help of kotlin delegate property "by"
    private val viewModel: ArticlesFeedViewModel by viewModels {
        ArticlesFeedViewModel.providesFactory(
            assistedFactory = articlesFeedViewModelFactory,
            userId = args.userId
        )
    }
 
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentArticlesFeedBinding.inflate(inflater, container, false)
        return binding.root
    }
     
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
         
        // Do something here
        // You can observe/handle your api response
       // here coming through ArticlesViewModel
    }


 

 

So, this way we’ll get our ViewModel injected with assisted dependency userId supplied at runtime. That’s how we use Assisted Dependency Injection with Dagger-Hilt that allows us to pass arguments at runtime to construct an object/dependency.

 



Similar Reads

Difference Between Dagger, Hilt, Koin and Pure Dependency Injection
It is often an overwhelming task to choose the most suitable dependency injection framework for your project, as there are a variety of popular and widely used frameworks on the market. Dagger, Hilt, Koin, and Pure Dependency Injection are some of the most popular frameworks currently in use. What is Dagger? Dagger is an open-source, lightweight de
5 min read
Dagger Hilt in Android with Example
Dependency Injection (DI) is a design pattern to decouple the conventional dependency relationship between objects. When it comes to DI in android Dagger always takes a lead. But it is very complex and requires a lot of boilerplate codes in order to set up the Dagger. So, to overcome this problem Hilt was introduced. Dagger Hilt simplifies the whol
8 min read
Dependency Injection with Dagger 2 in Android
If there are two classes, class A and class B and class A depends on class B then class B is called dependent for class A. So, Every time we want to access class B in class A we need to create an instance of class B in Class A or use static factory methods to access class A. But this will make our code tight coupled, difficult to manage, and test.
7 min read
Why to Use the Dependency Injection Framework like Dagger in Android?
In our project, we may or may not have used a dependency framework. In this blog, we will discuss why we need a dependency framework and why using one in our project is beneficial. We can assume that this is an interview question in this case. In this Geeks for Geeks article, we will respond to it by discussing the following topics: Why do we use a
5 min read
Manual Dependency Injection in Android
Android is an open-source operating system, based on the Linux kernel and used in mobile devices like smartphones, tablets, etc. Further, it was developed for smartwatches and Android TV. Each of them has a specialized interface. Android has been one of the best-selling OS for smartphones. Android OS was developed by Android Inc. which Google bough
3 min read
Dependency Injection in Android With Koin
Koin is an efficient dependency injection framework, to whom we delegate the duty of instantiating various objects of an application. We'll explore how Koin can assist us in efficiently managing these dependencies. Begin using Koin Koin is a really simple and easy framework, all we have to do is construct our classes and instruct Koin on how to cre
4 min read
MVVM (Model View ViewModel) Architecture Pattern in Android
Developers always prefer clean and structured code for projects. Organizing the codes according to a design pattern helps in the maintenance of the software. By having knowledge of all crucial logic parts of the android application, it is easier to add and remove app features. Further, design patterns also assure that all the codes get covered in U
7 min read
ViewModel in Android Architecture Components
ViewModel is part of the android architecture component. Android architecture components are the components that are used to build robust, clean, and scalable apps. Android architecture components hold some classes to manage UI components and Data persistence. The ViewModel class is designed to store and manage UI-related data in a lifecycle-consci
5 min read
Fragment to Fragment Communication in Android using Shared ViewModel
If there are two or more fragments in an activity, they need to communicate and share the data between them. The traditional way of sharing the data between the two fragments is implementing the callback using an interface that is cumbersome and may throw exceptions. But the modern way of doing that is using shared ViewModel. So in this article, it
6 min read
ViewModel With SavedState in Android
Google launched Android Jetpack at Google I/O 2018, which is a bundle of components, tools, and guidelines for creating excellent Android apps. It included LiveData, ViewModel, Room Database, Work Manager, and other components. ViewModel will be discussed in this blog. In the Activity lifecycle, ViewModel maintains UI-related data. It enables the A
5 min read