Switch to dagger

Why and how you should switch to Dagger 2.12+? By Gabriel Samojło

Posted on Posted in bitcraft, Know-how

Dependency Injection is good.

Yeah, we all know it. And yes, we were using it for a while. But were we using it correctly? Was it easy? Uhm. It depends. But one thing is sure: now it’s better.

Just to make it clear: it’s not a Dagger tutorial. If you are not familiar with it, I recommend for you to read a bit, as it’s great and you definitely should learn Dagger and start using it in your projects.

Okay. So what’s the deal with this all-new Dagger? Well… it came with Android support. As you know, Dagger is Java library, not the Android one. So all the stuff we were doing with Dagger was pure Java Dependency Injection. But was it wrong? Why it is a problem? It’s simple — many of Android framework classes are instantiated by Android itself so we had to perform injection inside of lifecycle methods. By that, we were copy-pasting pretty long builders into our onCreate() methods. I guess you know that:

((MyApplication) getApplication())
.getAppComponent()
.build()
.inject(this);

Pretty scary, right? It’s really not how dependency injection should look like. It obviously breaks one of the most important rules of it:
“A class shouldn’t know anything about how it is injected.”
Also, by copy-pasting we are repeating ourselves in many places. That is also wrong. Let’s change it!

Where to start? First of all, we will have to add or change dependencies in our app level build.gradle file:

compile ‘com.google.dagger:dagger:2.13’
compile ‘com.google.dagger:dagger-android-support:2.13’
annotationProcessor ‘com.google.dagger:dagger-compiler:2.13’
annotationProcessor ‘com.google.dagger:dagger-android-processor:2.13’

That was quick. Let real fun begin!

We will start at our component — let’s modify it a bit.
I guess it looks like this now:

@Component(modules = {AppModule.class})
public interface AppComponent {
    void inject(FirstActivity firstActivity); 
    void inject(SecondActivity secondActivity);
}

Version 2.12 of Dagger changed the way we are building our component interface. First of all, we will get rid all of those inject(Activity) methods (don’t worry, we will add them in a prettier form a bit later).

After that, we need to add one Dagger built-in module to our component modules. Let’s add AndroidSupportInjectionModule (if you are using support library) or AndroidInjectionModule. We should also extend our interface from AndroidInjector

So how our component looks like now?

@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class})
public interface AppComponent extends AndroidInjector {
  
}

That’s sweet, right?

But there is one more thing we need to do. Remember those builders in our onCreate() methods? Dagger comes with pretty neat simplification of it: say hi to the Component Builder!

Just add those magic lines to your Component:

@Component.Builder 
interface Builder { 
     @BindsInstance 
     Builder application(Application application);                      
     AppComponent build();
}

But what this magic spell does?
It’s pretty easy and clear: we are annotating that this interface will be responsible for our generated builder which will have methods responsible for retrieving our application class and building whole component together.
@BindsInstance means that this method will also inject our Application class to the component so we will be able to use it there. And that’s it.

So once again, our (almost) finished component will look like this:

@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class})
public interface AppComponent extends AndroidInjector {
  
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(Application application);
        AppComponent build();
    }
  
}

Now let’s modify our Application class a bit. First of all, we will need to extend it from DaggerApplication.
Be careful about proper import! If you are using support library you should extend DaggerApplication from support package.

As you see, now we will need to override applicationInjector() method. Don’t worry, it’s really simple and straightforward:

@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
    AppComponent appComponent = DaggerAppComponent.builder().application(this).build();
    appComponent.inject(this);
        
    return appComponent;
}

What are we doing here?
Let’s see.. we are using our auto-generated builder! Remember those fancy annotated thing in our Component? Good job, Dagger.

To sum up things: we have our Component and we modified our Application class. What else (and why) do we need to change? Well… ALL of our Activities. That’s because, as you remember, we don’t have our .inject(Activity) methods in the component.

We need to do two things in every activity which was injected like this.
– First of all: remove AppComponent.inject(Activity) method.
– The second: let’s make your Activity extends DaggerActivity (or DaggerAppCompatActivity).

Of course, if you are migrating to new Dagger in a big project it can take a while, but believe me — it’s worth it.

Now we need to do the last thing to make everything work together.
Let’s inject our Activities to proper Modules.

For that, I created a separate module for this called ActivityContributorModule. How will it look like?

@Module
public abstract class ActivityContributorModule {
  
    @ContributesAndroidInjector(modules = FirstActivityModule.class)
    abstract FirstActivity contributeFirstActivity();
  
    @ContributesAndroidInjector
    abstract SecondActivity contributeSecondActivity();
  
}

Looks familiar? Of course! We basically moved our .inject(Activity) methods to other place and change them to a bit more modern-looking format.

Remember: Every Activity which extends DaggerActivity will need to be annotated like that. We should also create such a component for every type of Android framework classes we are using for dependency injection in our project (like Fragment, IntentService, BroadcastReceiver etc)

But what does this annotation do? Again, it’s generating all necessary code responsible for injecting our activities into the module. Take a look at the modules value in the annotation. As you can see, we can even bind our activities to other modules. It will be useful when you will have to scope your dependencies!

Okay, we have the last thing to remember: don’t forget to add your contribution module to your component interface (just like you do with AppModule or AndroidInjectionModule)!

And that’s it! We successfully migrated to new Dagger. Was it hard? Not really. Was it worth it? Yes! Now we have our dependency injection set in a ‘proper’ way: our activity doesn’t know how its injected. We also get rid of annoying builders and injections in our onCreate()methods, so we are following dependency injection principles. In other words: we have more clean, more readable and more testable codebase.

Hope you enjoyed it!
Gabriel Samojło
Lead Android Developer @ BitCraft

If you want to see this example in a ‘proper’ way, take a look at my repo:
https://github.com/GabrielSamojlo/dagger-2.12

Original post: https://android.jlelse.eu/why-and-how-you-should-switch-to-dagger-2-12-ba32aac44300

Leave a Reply

Your email address will not be published. Required fields are marked *