Android Small Talks: Dependency Injection with Dagger 2

Android Small Talks: Dependency Injection with Dagger 2

Dependency injection is a design pattern the main task of which is to free our code of dependencies. As we all surely realize, code with a minimal amount of dependencies is far easier to manage and change. It’s also easier to use and test such code.

So, let’s see how we can apply this design pattern to the app.

Here is an example with a hidden dependency:

public class Lamp{
	Bulb bulb;
	public Lamp(){
		bulb = new Led();
	}
}

In this case, the Lamp class is dependent on the LED type bulb. We are not able to change it to any other type of bulb while the program is running or while testing the code. We can do it better:

public class Lamp{
	Bulb bulb;
	public Lamp(Bulb bulb){
		this.bulb = bulb;
	}
}

What does a possibility to create lamps with different light bulbs give us?

Lamp ledLamp = new Lamp(new Led());
Lamp halogenLamp = new Lamp(new Halogen());

It gives us the possibility to easily change implementation depending on our needs. You can read more about dependency injection here.

Platforms enabling code injection

Numerous libraries enable automation of the process of dependency injection. The most popular include:

  • guice,
  • dagger,
  • dagger2.

Each of these frameworks works on Android.

Dagger 2

Dagger 2 is a library created by Google. Its main advantages include:

  • generating code during compilation,
  • validation of initialization classes during compilation,
  • major readability of generated classes,
  • an option of changing implementation for testing.

Dagger 2 connects very well with Gradle. To start playing with dependency injection we need to add the code below to the main build.gradle file of our project:

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

Next, we need to add a few lines to the build.gradle file of our module (code has been shortened for clarity):

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'

android {
    ...
}
dependencies {
    apt 'com.google.dagger:dagger-compiler:2.0'
    testApt 'com.google.dagger:dagger-compiler:2.0'
    androidTestApt 'com.google.dagger:dagger-compiler:2.0'
    provided 'org.glassfish:javax.annotation:10.0-b28' //Required by Dagger2 
}

Dagger 2 uses popular annotation @Inject, which is defined in javax.annotation packages. Android doesn’t implement these annotations, so we need to provide them ourselves. Then we can get to writing code! 🙂

Component

It is the main element of the platform. Components enable specifying to which classes code will be injected and what modules will provide dependencies. An example of a component that provides us application context looks like this:

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
    Context context();

    Application application();
}

This component tells us only what dependencies it will be providing. Implementation of this interface will be generated by Dagger 2 during compilation.

The component is the class marked by Component annotation. In the annotation, we can mention modules that provide us with the declared objects, or other components on which the module is dependent (in this case ApplicationComponent is not dependent on any other components).

In the components, we can define for how long we want to store objects. This is done with:@Singleton annotation that tells Dagger that objects declared in this component are to be stored while an application is running.

In such a simple way we can define in our application a singleton, which will be resistant to running activity more than once. All objects that are to be singletons must be declared in the component marked with the @Singleton annotation.

Another component is already designed to inject dependencies:

@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
    void inject(MainActivity activity);
}

A special feature inject() defines classes into which dependencies will be injected. It is important to use here class implementations and not base classes so that Dagger knows on which object to operate. In the discussed example there was declared a dependency on ApplicationComponent. Thanks to that injected class can use objects declared in the dependent component.

Modules

Modules provide implementations of objects. However, we must remember to create objects that have no implementation (for example, objects from the library). The application module is as follows:

@Module
public class ApplicationModule {
    protected final Application application;

    public ApplicationModule(Application application) {
        this.application = application;
    }

    @Provides
    Application provideApplication() {
        return application;
    }

    @Provides
    Context provideContext() {
        return application;
    }
}

All functions that are annotated with @Provides will be used by Dagger to create objects. In this way, you can, for example, create an object of an API client.

Initialization of components

You should remember that implementations of components will not be available until the first build-up of the project. They also have special names: DaggerNameOfComponent. To initialize an application component you need to add the following code in your application:

public ApplicationComponent getComponent() {
        if (applicationComponent == null) {
            applicationComponent = DaggerApplicationComponent.builder()
                    .applicationModule(new ApplicationModule(this))
                    .build();
        }
        return applicationComponent;
    }

In the example above, DaggerApplicationComponent is a class generated by Dagger. Similarly, we need to add code in the activity:

activityComponent = DaggerActivityComponent.builder()
                    .applicationComponent(MyApp.get(this).getComponent())
                    .activityModule(new ActivityModule(this)).build().inject(this);
After executing this code, in MainActivity object and in all classes owned by the activity our objects will be created. 

When you want a created object to participate in process of injection you need to mark its constructor – @Inject annotation:

public class DIObject {
  
    @Inject
    public DIObject() {
    }
}

Every time you want to use an object instance you just need to create:

@Inject
DIObject object

Thanks to that Dagger will know how to deliver an object to us.

And thanks to using dependency injection, the code that we write will become much more transparent. All objects will be created in a separate class, which will eliminate multiple repetitions. Our tests will be simpler and code easier to maintain.

You can check a sample code illustrating this technique on our GitHub.

Learn more

Bitrise Tests Made Easier: Update JIRA Issues with Build Number and Forget About Delays in QA Testing

When you trigger Bitrise build with changes and forget to tell QA specialists some essential information, there are two likely scenarios. You waste time waiting for the update from the tester, only you don’t know it’s never coming. Or you get so many questions about builds that you can’t keep up. Sounds familiar? If so, let us tell you about the JIRA issue update that keeps workflow in order.

Read more

ConstraintLayout, or not so fast, after all…

Recently, while browsing through news from Android world I came across the concept of ConstraintLayout. It is a new layout delivered by Android and Google, supporting Android versions from API 9 on. Digging into possibilities it is to give, I decided to check how the new Layout Builder behaves and what is ConstraintLayout like in use.

Read more

Android Small Talks: CoordinatorLayout, or one of the strengths of Android Design Support Library

When Android Lollipop entered the market, there was a breakthrough. Google provided us with an extensive library of Android Design Support Library which facilitates creating applications that are compliant with the principles of Material Design. Creating a user interface in accordance with these guidelines introduces our software to the next level of design and user-application interaction.

Read more

Project estimation

Let us know what product you want to build and how we can help you.

Why choose us?

Logo Mobile Trends Awards

Mobile Trends Awards 2021

Winning app in
EVERYDAY LIFE category

Nagroda Legalnych Bukmacherów

Legal Bookmakers Award 2019

Best Mobile App

Mobile Trends Awards logo

Mobile Trends Awards 2020

Nomination in SPORTS & RECREATION category

20

client reviews

Clutch logo