Using Dagger 2 for dependency injection in Android - Tutorial

This article describes the usage of Dagger 2 within standard Java applications and within Android applications.

1. Introduction to the concept of dependency injection

See Dependency Injection for an introduction into the concept of dependency injection.

2. Dependency injection with Dagger 2

2.1. What is Dagger 2?

Dagger 2 is dependency injection framework. It is based on the Java Specification Request (JSR) 330. It uses code generation and is based on annotations. The generated code is very relatively easy to read and debug.

Dagger 2 uses the following annotations:

  • @Module and @Provides: define classes and methods which provide dependencies

  • @Inject: request dependencies. Can be used on a constructor, a field, or a method

  • @Component: enable selected modules and used for performing dependency injection

In Dagger it is not allowed to use private fields for field injection as Dagger 2 uses generated code to access the fields and not reflection.

2.2. Defining dependency providers (object providers)

The term dependency injection context is typically used to describe the set of objects which can be injected.

In Dagger 2, classes annotated with @Module are responsible for providing objects which can be injected. Such classes can define methods annotated with @Provides. The returned objects from these methods are available for dependency injection.

Methods annotated with @Provides can also express dependencies via method parameters. These dependencies are fulfilled by Dagger 2, if possible.

2.3. Defining dependencies (object consumers)

You use the @Inject annotation to define a dependency. If you annotate a constructor with @Inject, Dagger 2 can also use an instance of this object to fulfill dependencies. This was done to avoid the definition of lots of @Provides methods for these objects.

2.4. Connecting consumers and providers

The @Component is used on an interface. Such an interface is used by Dagger 2 to generate code. The base pattern for the generated class is that Dagger is used as prefix followed by the interface name. This generate class has a create method which allows configuring the objects based on the given configuration. The methods defined on the interface are available to access the generated objects.

@Component interface defines the connection between provider of objects (modules) and the objects which expresses a dependency. The following table gives an overview of the usage of the Dagger annotations.

Table 1. Annotation summary of Dagger 2
Annotation Usage

@Module

Used on classes which contains methods annotated with @Provides.

@Provides

Can be used on methods in classes annotated with @Moduleand is used for methods which provides objects for dependencies injection.

@Singleton

Single instance of this provided object is created and shared.

@Component

Used on an interface. This interface is used by Dagger 2 to generate code which uses the modules to fulfill the requested dependencies.

2.5. Scope annotations

You can use the @Singleton annotation to indicate that there should be only one instance of the object.

2.6. Special treatment of fields in Dagger

Dagger 2 does not inject fields automatically. It can also not inject private fields. If you want to use field injection you have to define a method in your @Component interface which takes the instance into which you want to inject as parameter.

2.7. Using Dagger 2 with Eclipse and Maven

To use Eclipse and Maven together with Dagger 2 you can install the Maven tooling and the apt plug-in which allows Maven to configure the annotation processors. For Eclipse Maven support use the update site of your release and afterwards install the m2e-apt tooling via the http://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt update site.

After the installation you must enable apt-processing on via the Eclipse preferences in Window ▸ Preferences ▸ Maven ▸ Annotation Processing. Select Automatically configure JDT APT.

Maven apt processing configuration

3. Exercise: Dependency injection with Dagger 2

In this example you use Dagger 2 in a standard Java program managed by Maven.

This exercise can be done via any Java IDE you like, for example the Eclipse IDE with the Maven tooling installed. Or it can be developed and compiled via a text editor and the Maven command line. If you use Eclipse, ensure to install the Eclipse Maven tooling.

3.1. Create example project for the usage of Dagger 2

Create a new Maven project with the com.vogella.java.dagger2 top level package.

3.2. Define or adjust Maven build file

Adjust your pom.xml to the following.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.vogella.java.dagger2</groupId>
    <artifactId>com.vogella.java.dagger2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>com.vogella.java.dagger2</name>

    <dependencies>
        <dependency>
            <groupId>com.google.dagger</groupId>
            <artifactId>dagger</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>com.google.dagger</groupId>
            <artifactId>dagger-compiler</artifactId>
            <version>2.4</version>
            <optional>true</optional>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3.3. Define classes which have a dependency

Define the following two classes which express dependencies.

package com.vogella.java.dagger2;

import javax.inject.Inject;

public class User {

        private String firstName;
        private String lastName;

        public User(String firstName, String lastName) {
                this.firstName = firstName;
                this.lastName = lastName;
        }

        @Override
        public String toString() {
                return "User [firstName=" + firstName + ", lastName=" + lastName + "]";
        }
}
package com.vogella.java.dagger2;

import javax.inject.Inject;

public class BackendService {

        @Inject
        public User user;

        private String serverUrl;

        @Inject
        public BackendService(@Named("serverUrl") String serverUrl) {
                this.serverUrl = serverUrl;
        }

        public boolean callServer() {
                if (user !=null && serverUrl!=null && serverUrl.length()>0) {
                        System.out.println("User: " + user + " ServerUrl: "  + serverUrl);
                        return true;
                }
                return false;
        }
}

3.4. Define modules which provides dependencies

Define two class which acts as providers for dependencies.

package com.vogella.java.dagger2.modules;

import javax.inject.Named;
import javax.inject.Singleton;

import com.vogella.java.dagger2.BackendService;

import dagger.Module;
import dagger.Provides;

@Module
public class BackEndServiceModule {

    @Provides
    @Singleton
    BackendService provideBackendService(@Named("serverUrl") String serverUrl) {
       return new BackendService(serverUrl);
    }

    @Provides
    @Named("serverUrl")
    String provideServerUrl() {
       return "http://www.vogella.com";
    }

    @Provides
    @Named("anotherUrl")
    String provideAnotherUrl() {
       return "http://www.google.com";
    }

}
package com.vogella.java.dagger2.modules;

import javax.inject.Singleton;

import com.vogella.java.dagger2.User;

import dagger.Module;
import dagger.Provides;

@Module
public class UserModule {

    @Provides
    @Singleton
    User providesUser() {
            return new User("Lars", "Vogel");
    }
}

3.5. Define components

Components define from which modules (or other components) dependencies are provided. Dagger 2 uses this interface to generate the accessor class which provides the methods defined in the interface.

package com.vogella.java.dagger2.component;

import javax.inject.Singleton;

import com.vogella.java.dagger2.BackendService;
import com.vogella.java.dagger2.modules.BackEndServiceModule;
import com.vogella.java.dagger2.modules.UserModule;

import dagger.Component;

@Singleton
@Component(modules = { UserModule.class, BackEndServiceModule.class })
public interface MyComponent {

    // provide the dependency for dependent components
    // (not needed for single-component setups)
        BackendService provideBackendService();

        // allow to inject into our Main class
        // method name not important
        void inject(Main main); 
}
Method which allows injecting into BackendServer for the fields

3.6. Use the generated dagger code

The usage of the generated code is demonstrated by the following test class.

package com.vogella.java.dagger2.main;

import com.vogella.java.dagger2.BackendService;
import com.vogella.java.dagger2.component.DaggerMyComponent;
import com.vogella.java.dagger2.component.MyComponent;

public class Main {

    @Inject
    BackendService backendService;

    private MyComponent component;

    private Main() {
        component = DaggerMyComponent.builder().build();
        component.inject(this);
    }

    private void callServer() {
        boolean callServer = backendService.callServer();
        if (callServer) {
            System.out.println("Server call was successful. ");
        } else {
            System.out.println("Server call failed. ");
        }
    }

        public static void main(String[] args) {
        Main main = new Main();
        main.callServer();
        }
}
Fields are not automatically injected hence the method call to inject the fields

4. Dagger 2 and Android

4.1. Using dependency injection with Android

Many Android components, e.g. activities, are instantiated by the Android framework and not in your code. This makes it difficult to supply dependencies via constructors to Android components.

4.2. Using Dagger 2 in Android

To enable Dagger 2 adjust your build.gradle project file.

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0-alpha3'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Add the following entries to your app/build.gradle file.

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

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0"

    defaultConfig {
        applicationId "com.vogella.android.dagger2simple"
        minSdkVersion 22
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

ext {
    JUNIT_VERSION = '4.12'
    DAGGER_VERSION ='2.4'
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.dagger:dagger:2.4'
    apt "com.google.dagger:dagger-compiler:DAGGER_VERSION"
    provided 'javax.annotation:jsr250-api:1.0'
    compile 'javax.inject:javax.inject:1'
    testCompile "junit:junit:$JUNIT_VERSION"
    testApt "com.google.dagger:dagger-compiler:DAGGER_VERSION"
}

You need to configure apt for each scope in which Dagger should be used. For example apt for you normal app, testApt for your JVM tests and testAndroidApt for your Android tests.

5. Exercise: Dependency injection in Android with Dagger 2

5.1. Target of this exercise

In this exercise Dagger 2 is demonstrated in an Android application. In our simple app, there is an activity that allows a user to authenticate some credentials. The result value of the implementation is displayed in a text field.

5.2. Create project

Create a new project with the top level package com.vogella.android.dagger2simple.

Adjust the layout file of the generated activity.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/target"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

5.3. Enter Gradle dependencies

Add the following entries to your build.gradle project file.

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0-alpha3'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Add the following entries to your app/build.gradle file.

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

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0"

    defaultConfig {
        applicationId "com.vogella.android.dagger2simple"
        minSdkVersion 22
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

ext {
    JUNIT_VERSION = '4.12'
    DAGGER_VERSION ='2.4'
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.dagger:dagger:2.4'
    apt "com.google.dagger:dagger-compiler:DAGGER_VERSION"
    provided 'javax.annotation:jsr250-api:1.0'
    compile 'javax.inject:javax.inject:1'
    testCompile "junit:junit:$JUNIT_VERSION"
    testApt "com.google.dagger:dagger-compiler:DAGGER_VERSION"
}

5.4. Define your dependencies graph

Create the following classes for providing dependencies and wiring them up.

package com.vogella.android.dagger2simple;

public class NetworkApi {

    public boolean validateUser(String username, String password) {
        // imagine an actual network call here
        // for demo purpose return false in "real" life
        if (username == null || username.length() == 0) {
            return false;
        } else {
            return true;
        }
    }
}
package com.vogella.android.dagger2simple.modules;

import com.vogella.android.dagger2simple.NetworkApi;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

@Module
public class NetworkApiModule {
    @Provides
    @Singleton
    public NetworkApi getNetwork(){
        return new NetworkApi();
    }
}
package com.vogella.android.dagger2simple.components;

import android.app.Activity;

import com.vogella.android.dagger2simple.MainActivity;
import com.vogella.android.dagger2simple.NetworkApi;
import com.vogella.android.dagger2simple.modules.NetworkApiModule;

import javax.inject.Singleton;

import dagger.Component;

@Singleton
@Component(modules = {NetworkApiModule.class})

public interface DiComponent {
    // to update the fields in your activities
    void inject(MainActivity activity);
}

5.5. Wiring up the application

Create the following implementation for your Application class. This class builds up the dependency injection context and gives access to it, via the getComponent method.

package com.vogella.android.dagger2simple;

import android.app.Application;

import com.vogella.android.dagger2simple.components.DaggerDiComponent;
import com.vogella.android.dagger2simple.components.DiComponent;

public class MyApplication extends Application {
    DiComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerDiComponent.builder().build();
    }

    public DiComponent getComponent() {
        return component;
    }
}

Of course this class needs to registered via the manifest.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.vogella.android.dagger2simple" >

    <application
        android:name="MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

The activity injects the created values into it via the method defined on the @Component class.

package com.vogella.android.dagger2simple;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

import javax.inject.Inject;

public class MainActivity extends Activity {

    @Inject NetworkApi networkApi;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ((MyApplication) getApplication()).getComponent().inject(this);

        boolean injected =  networkApi == null ? false : true;
        TextView userAvailable = (TextView) findViewById(R.id.target);
        userAvailable.setText("Dependency injection worked: " + String.valueOf(injected));
    }
}

5.6. Validate

Run your application, the user interface should report that dependency injection worked. If not, have a look at the generated classes in the app/build/generated/apt folder. The generated classes by Dagger are well structured and should give a hint what went wrong.

Running application

6. Replacing @Module classes in tests

For tests you can your custom modules and setup Dagger to use them. The generated builder of Dagger contains methods to set the module. You can use the Dagger build to replace the generated class with the one which should be used for testing.

This also works well with Mockito. Assume you have the following class you want to test.

public class ImportantClass {
    private MyRestService restService;
    private MyLogger logger;

    @Inject public MainService(MyRestService restService, MyLogger logger) {
        this.restService = restService;
        this.logger = logger;
    }

    public void perform() {
        String s = restService.getData();
        logger.write(s.toUpperCase());
    }
}

You can test this class with Mockito.

public class ImportantClassTest {

public class MainServiceTest {

    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock MyRestService restService;

    @Mock MyLogger logger;

    @InjectMocks ImportantClass importClassToBeTested;

    @Test
    public void performShouldWriteOutput() {
        when(restService.getData()).thenReturn("abc");

        importClassToBeTested.perform();

        verify(logger).print("ABC");
    }
}
}

Better support for replacing Dagger dependencies is offered by https://github.com/fabioCollini/DaggerMock.

7. About this website

Support free content

Support free tutorials

Questions and discussion

Questions and discussion

Tutorial & code license

License

Get source code

Source Code

8. Dagger resources

8.1. vogella GmbH training and consulting support

TRAINING SERVICE & SUPPORT

The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值