In previous blog, we used plain dagger to do the dependency injection on Android, but there is another package from Google named
dagger-android, and it’s tailed for Android. Things get more interesting here. Let’s see.
Here is another blog for mocking dagger in Espresso test if you need it.
If you don’t know what dagger is as well as dependency injection, and the some basic dagger terms like
component seem secret to you. I strongly suggest you to read my other blog first, which is an implementation with plain dagger. Because that is easier to understand and just take you minutes. Then you can come back and see this blog for a more Android approach and to see which pattern you like most.
When Google writes
dagger-android, they want to reduce the boilerplate code you need to write with plain
dagger. So they introduce some new abstraction. And it’s very easy to get lost here. So I think it might be better that we review this base pattern first. As I said in the previous blog. In order to do DI, you need to prepare these initialization of dependencies somewhere before you can use them. So here, in dagger’s terms:
- You declare how to generate these dependencies in
- You use
@Componentto connect the dependencies with their consumers.
- Then inside the consumer class. You
daggerwill create the instances from the
First, let’s create an application wide
@Providesis the provider for any consumer which has the
@Injectdecorator. Dagger will match the type for you. Which means when a
@Injectasks for the type
SharedPreferences, dagger will find through all
@Provideand find a method which return type matches and get that instance from this method.
But something interesting here is that who will give that
app parameter when calling this
provideSharedPreference() method and where does it get it. Well, we will see that soon.
Now consider this is an application wide dependency, we will connect this
@Module to the android
application with a
@Component. For those who has
Vue experiences, it’s not that
component at all. :D
Here, something different from the plain dagger is that this
interface is based on another
AndroidInjector<App>. And needs to declare its
Builder as well. The
App here is your custome
application class. We will create that later on.
Builder is for dagger, so it knows how to
create() it. For instance, in our example, we need to the caller to pass
create(). No magic here, as the signature indicates, you have to pass it when you first invoke this method.
@BindsInstance is where it shines. It will take this incoming
app parameter, and save it for using later, such that, in our previous
AppModule, you can invoke
provideApplication() with a parameter called
app. And this is how it gets that parameter. Because the
app has been
@BindsIntance when the
AndroidSupportInjectionModule::class is from dagger to inject into other types other than your
And good question here will be why this is an
interface, well, because overall, dagger is more like a code generation than a library. It’s an
interface because dagger will generate the actual implementation for you as long as you declare all the things they want and in their way.
This is your normal custom
Don’t forget to add this
App to the
android:name= attribute to enable it.
Something interesting is that we extend the App from
DaggerApplication to reduce some boilerplate like before, the only thing you need to do is to override that
applicationInjector method, initialize and return your
then you call the
create() method which you created in that
Builder interface, passing
this which just fits the signature:
fun create(app: Application):Builder.
DaggerAppComponentwill be unresolved until you run
Make Projectfrom the
If you don’t want to inherit from
DaggerApplication, you have to implement the
You need to know that
DaggerApplicationdoes much more things for you other than the several lines of boilerplate above. It handles things like
BroadCastReceiverto make your life easier in the future.
Create a new file
ActivitiesBindingModule.kt with the following code:
If you want to add more activities, just add them here use the same pattern.
Now connect the
ActivitiesBindingModule into the
If you went through my previous blog, you will notice this part is different. You no longer declare a method:
fun inject(activity: MainActivity). You need to use a
ActivitiesBindingModule to do the trick. But still you can write
component for that activity. this is just easier.
It’s more clean than before, you use
@Inject, then you get the it. No more
(application as MyApp).myAppComponent.inject(this).
Run the app, you should see something like this in the console:
The magic could happen only because we inherited from
DaggerAppCompatActivity, other wise you need to call
AndroidInjection.inject(this) in the
onCreate by yourself.
Let’s see that you need some dependencies that is only for one activity. Here for example, we need one such thing for the
Then we just inject in and use it in
Where to get this
abcKey initialized? Well, we create a
And connect it with the
Run the app, you should see
value of abcKey: false printed in the console.
provideABCKey(preference:SharedPreferences), it needs a
SharedPreferences. How could dagger get it?
Well, with all the setup, dagger has a graph of all your dependencies. And every time it needs a parameter in a
@Provides function, it will check other
@Provides functions to look for that type. In our case, it will find it from the
provideSharedPreference() and get it from there. Much better, it’s a singleton! No new instance created!
And this is a very important feature, remember the moments where you need to create an instance A in order to create instance B. And order for initialization matters only because there is a dependency chain? Well, they are all on
dagger now. :)
When we wrote that
MainActivityModule class. Anytime when dagger tries to get that instance, it needs to create an instance of
MainActivityModule first. That is not very good. We can make it better by make that
@Provides function static. In Kotlin, the syntax would be:
Now, the dagger could invoke this method like this:
MainActivityModule.provideABCKey() without the need of a new instance.
Why make the class
abstract? Well, this is for a better check, because now if any
@Providesin this module is non-static, dagger will give you a compile time warning.
Furthermore, you can move this code into
MainActivityModule class as well:
Then, you can remove that
ActivitiesBindingModule.kt. But add this
MainActivityModule to the
AppComponent. Such that, everything belongs to
MainActivity DI now resides in one place.
Which approach do you like? :) Sometimes, some activities may only want an application-wide injection. Which seems too much to create a
@Module for it. Maybe a combination of both? But that means sometimes, something is here and sometimes something is there. So, might be better just use the current setup. :) But making methods
static. What do you think? :)
You can find the repo here
Hope it helps.