Explaining Dependency Injection with Dagger 2 – Android

Dagger 2 is a dependency injection framework.

Example of a Dependency

Below is an example where we have Class A which requires and instance of another class.

public class A {
  private B b;
  public A() {
    b = new B();
  }
}

There are a few issues with above code:

  • Class A implementation depends of constructor of Class B. E.g if constructor of class B changes, we need to update variable “b” initialization.
  • If have to use same instance of variable B in several classes, we have to duplicate same initialization code in other classes’s constructor (e.g onCreate method – Android).

The idea and aim when using dependency injection is to “decouple” implementations, making class A less dependent on Class B’s implementation. E.g if we need to change implementation of B, the implementation code for Class A will not require any change.

One way to achieve this is to declare class’s B initialization in another class so that all classes which require object will get it from there. That’s what a dependency injection framework does, but in a clean and more robust way.

Getting started with Dagger 2 on Android

On Android, as an example, we would often instance of DB manager or HttpClient inside many of our Activities/Presenter. We could use “static” methods/objects as well, but DI is the “right” way and can be more reliable. So let’s take a deeper look at how to get started with Dagger 2.

Step 1: Gradle import

First let’s add this to the app module’s gradle file:

compile 'com.google.dagger:dagger:2.10'
annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.10'

Step 2: Create packages

I would advise to create a package named “di”. Inside of “di”, inside we will have two packages: “component” and “module”.

Example:

Screen Shot 2017-08-24 at 1.29.11 PM.png

 

Step 3: Create Module class:

Let’s create “ContextModule” class inside “di/module” package

@Module
public class ContextModule {

    private final Context context;

    public ContextModule(Context context) { // Constructor
        this.context = context;
    }

    @Singleton
    @Provides
    public Context getContext() { // Provides means Context objects to any method inside Module class or for injecting in other classes
        return context;
    }
}

Dagger 2 uses the following annotations:

  • @Module: define classes which provide dependencies
  • @Provides: define method which provide dependencies. E.g Context objects will be provided by this method.

Step 4: Building your project and Wiring up DI inside Application Class

After defining the module, you will need to build your project (may require clean as well). There will be some generated classes in our example, it will be for “ContextModule”. After building project, go to you application class, and add following:

appComponent = DaggerAppComponent.builder()
        .contextModule(new ContextModule(getApplicationContext()))
        .build();

For all modules, we create an instance by providing the constructor

Step 5: Define Component

Now we need to list of Activities/Classes which will require all dependencies we defined in our “Modules” classes (in our example, we have only “Application Context”).

@Singleton @Component(modules = {ContextModule.class,NetworkModule.class,DatabaseModule.class}) public interface AppComponent { void inject(BaseFragment fragment); void inject(BasePresenter presenter); void inject(BaseActivity activity); } What I do, I will have Base Classes for my Activities/Fragments/Presenter, e.g “BaseActivity” where we will need to do an additional wiring so that objects can be injected on e.g any Activities which extend BaseActivity.

Step 6: Wiring on Base Activities/Fragments/Presenters

public class BasePresenter implements BaseContract.BasePresenter {

    @Nonnull
    BaseContract.AppBaseView mView;

    @Inject
    protected Context context; // We are injecting the context provided from Application Class

    public BasePresenter(@Nonnull BaseContract.AppBaseView mView) {
        this.mView = mView;

        MApplication.getAppComponent().inject(this); // Wiring dependencies so that we can inject objects from Module class

        mSubscription = new CompositeSubscription();

        LogUtils.showLogDebug("subscribe");
    }
   .
   .
   .

-> MApplication.getAppComponent().inject(this);

This is how we wire the BasePresenter to Dagger so that we can inject all dependencies define in our module classes.

If you used “ButterKnife” library for injecting layout elements into Activities/Fragments classes, you know you have to call “ButterKnife.bind(this)” after Activity is created. ButterKnife also uses DI for injecting the layout items into related java/activity class.

That’s it. Now for any classes which extend the BasePresenter, you will be able to inject all dependencies defined.

Below is another example for BaseActivity

public abstract class BaseActivity extends AppCompatActivity implements BaseContract.BaseView {

    @Nonnull
    MgProgressBasePresenter mPresenter; 
    
    @Inject
    protected Context context; // We are injecting the context provided from Application Class

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getResourceLayout());
        ButterKnife.bind(this);

        MApplication.getAppComponent().inject(this); // "Binding" dagger dependency

        onViewReady(savedInstanceState);
        mPresenter = new BasePresenter(this);
    }

Too much boilerplate?

We were injecting only the Context class which might not seem very useful, but imagine if you had a Database Module, a Network module, and these constructors will be defined in only one place.

You can have a look at the Network Module for example:

@Module
public class NetworkModule {

    private String baseUrl;

    public NetworkModule(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    @Singleton
    @Provides
    public OkHttpClient.Builder getHttpClient() {
        return new OkHttpClient.Builder();
    }

    @Singleton
    @Provides
    public Retrofit.Builder getRetrofitBuilder() {
        return new Retrofit.Builder();
    }

    @Singleton
    @Provides
    public APIService getService(Context context, OkHttpClient.Builder httpClient, Retrofit.Builder retrofit) {
        .
        .
        .
    }

Please note here, we have getHttpClient() method, and getRetrofitBuilder() method which provides instances of OkHttpClient.Builder and Retrofit.Builder retrofit which will provide for instances for required arguments inside “getService(…)” method within the Module. So we are defining how the arguments inside getService will get it’s instances. Same apply for Context which shall be obtained from the ContextModule.

So now on our presenters (or fragments), we can inject another object without requiring any additional “wiring up” on our classes:

@Inject
APIService apiService;

I guess that’s all for today. Let me know if there is anything missing or any issue which you are getting with using Dagger 2 on Android. Catch you soon with another post.

Cheers,

One thought on “Explaining Dependency Injection with Dagger 2 – Android

Please Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s