Injecting an interface with Dagger 2 in a multi-module Android project

Kelvin Watson
2 min readFeb 25, 2020

--

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: :appand :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 Personto the interface PersonContract.

Please consider supporting me so I can bring you more content:

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Kelvin Watson
Kelvin Watson

Responses (1)

Write a response