Injecting a repository into a Service in Android using Hilt

13,033

Solution 1

From the documentation on how we Inject dependencies into Android classes, we can learn the following:

Hilt can provide dependencies to other Android classes that have the @AndroidEntryPoint annotation.

Hilt currently supports the following Android classes:

  • Application (by using @HiltAndroidApp)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

So when you subclass any of these Android classes, you don't ask Hilt to inject dependencies through the constructors. Instead you annotate it with @AndroidEntryPoint, and ask Hilt to inject its dependencies by annotating the property with @Inject:

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { 

    @Inject
    lateinit var mAdapter: SomeAdapter 

    ...

}

So, in your case you should inject MyRepository in MyActivity and MyService like this:

@AndroidEntryPoint
class MyService: Service() {

    @Inject
    lateinit var myRepository: MyRepository
   
    ...

}

@AndroidEntryPoint
class MyActivity: AppCompatActivity(R.layout.my_layout) { 

    @Inject
    lateinit var myRepository: MyRepository

    ...

}

And remember:

Fields injected by Hilt cannot be private

That's it for Android classes that is supported by Hilt.

If you wonder what about classes not supported by Hilt (ex: ContentProvider)?! I recommend learning how from this tutorial @EntryPoint annotation on codelab (also don't forget to check the documentation for how to Inject dependencies in classes not supported by Hilt).

Solution 2

Also If you overriding onCreate in your Service - don't forget to add super.onCreate() otherwise you will get runtime exception that the myRepository value cannot be initialised (I was struggling with this error for some time during refactoring my service so maybe it will be helpful for somebody)

@AndroidEntryPoint
class MyService : Service() {
    @Inject
    lateinit var myRepository: MyRepository

    override fun onCreate() {
        super.onCreate()

    }
}

Solution 3

Your use of @Inject on the MyService class is as if MyService is to be injected at some other location.

If I understand correctly, you want something more akin to:

@AndroidEntryPoint
class MyService : Service() {

    @Inject
    lateinit var myRepository: MyRepository

}
Share:
13,033

Related videos on Youtube

MikkoP
Author by

MikkoP

I'm a student living in Finland, interested in mathematical subjects, programming and computers.

Updated on June 04, 2022

Comments

  • MikkoP
    MikkoP about 2 years

    I have an Android project with Hilt dependency injection. I have defined MyApplication and MyModule as follows.

    @HiltAndroidApp
    class MyApplication : Application()
    
    @Module
    @InstallIn(ApplicationComponent::class)
    abstract class MyModule {
        @Binds
        @Singleton
        abstract fun bindMyRepository(
            myRepositoryImpl: MyRepositoryImpl
        ): MyRepository
    }
    

    MyRepositoryImpl implements the MyRepository interface:

    interface MyRepository {
        fun doSomething(): String
    }
    
    class MyRepositoryImpl
    @Inject
    constructor(
    
    ) : MyRepository {
        override fun doSomething() = ""
    }
    

    I can now inject this implementation of MyRepository into a ViewModel:

    class MyActivityViewModel
    @ViewModelInject
    constructor(
        private val myRepository: MyRepository,
    ) : ViewModel() { }
    

    This works as expected. However, if I try to inject the repository into a service, I get an error java.lang.Class<MyService> has no zero argument constructor:

    class MyService
    @Inject
    constructor(
        private val myRepository: MyRepository,
    ): Service() { }
    

    The same error occurs with an activity, too:

    class MyActivity
    @Inject
    constructor(
        private val myRepository: MyRepository,
    ) : AppCompatActivity(R.layout.my_layout) { }
    

    What am I doing wrong with the injection?

    • Bek
      Bek almost 4 years
      You can't use constructor injection with Activity or Service. Use field injection.
    • Sijan Neupane
      Sijan Neupane over 2 years
      can you provide your github link to this project? i'm learning DI and getting confused in repository and repositoryImpl. I'd be very thankful!
  • Сергей
    Сергей about 3 years
    Thanks bro! I suffered for a very long time in search of an answer. Yes, I removed super () and was getting this error
  • tufekoi
    tufekoi about 2 years
    A complement to this answer because I ran in the same error: do not use the injected variable inside onCreate, it will crash. The variable is safe to use from onStartCommand and later.