Injecting an interface with Dagger 2 in a multi-module Android project
In a multi-module Android project, you can use Dagger 2 to inject an interface, even if one module cannot access the implementation that lives in another module.
In this article, I’ll show you how to access a concrete implementation in an upstream module.
This article assumes some knowledge in Android development and familiarity with the Dagger 2/Dagger-Android library.
Let’s go through an example
Let’s say you have an Android project consisting of 2 modules: :app
and :myModule
. In our example,:app
depends on :myModule
.
In your :app
’s build.gradle:
dependencies {
implementation project(':myModule')
}
Then, let’s say that :app
contains the class Person
:
class Person @Inject constructor()
Let’s suppose that now, you want to access Person
in :myModule
. However, that’s not possible. Remember that :app
depends on :myModule
and not the other way around.
Expose the interface, not the implementation
How can we access a concrete implementation ofPerson
from :myModule
?
Create an interface in :myModule
:
/**
* Interface for use in Dagger 2
*/
interface PersonContract
Have Person
in the :app
module implement the interface:
class Person @Inject constructor() : PersonContract
Remember that :app
has access to :myModule
's classes
Now, expose Person
in one of your :app
’s Dagger modules
@Module
abstract class AppModule {
@ActivityScope
@ContributesAndroidInjector(modules = [MyActivityModule::class])
abstract fun contributeMyActivityInjector(): MyActivity
@Binds
abstract fun bindPersonContract(person: Person): PersonContract
}
Now, assuming you have Dagger injection set up in both :app
and :myModule
, you can now inject a Person
in :myModule
by simply injecting PersonContract
.
package com.example.mymoduleclass MyActivity : AppCompatActivity(), HasAndroidInjector {
// This will be the concrete instance
@Inject
lateinit var person: PersonContract
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any>
override fun androidInjector(): AndroidInjector<Any> = androidInjector
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
}
}
What is happening here?
:myModule
does not have access to Person
. However, Dagger 2 still knows how to construct a Person
since we bound the Person
to the interface PersonContract
.