Android Dagger2 Framework Tutorial and Examples

What is Dagger2?

Dagger2 is a static compile-time dependency injection Framework for Java,Kotlin and Android.

It should actually be Dagger, Dagger2 simply implies the second version which was a complete re-write of the DI framework. The earlier version was created by Square. Dagger2 is now maintained by Google.

Currently it’s one of the most popular Dependency Injection Frameworks and there are a bunch of tutorials and frameworks all over the internet.

However in this guide we will look at several examples starting from beginner level and slowly building to intermediate ones. To view the examples scroll down to the examples section.

Dagger2 Best Practices

Here are some best practices recommended in the Android Documentation regarding Dagger2

  • Use constructor injection with @Inject to add types to the Dagger graph whenever it’s possible. When it’s not:
    • Use @Binds to tell Dagger which implementation an interface should have.
    • Use @Provides to tell Dagger how to provide classes that your project doesn’t own.
  • You should only declare modules once in a component.
  • Name the scope annotations depending on the lifetime where the annotation is used. Examples include @ApplicationScope, @LoggedUserScope, and @ActivityScope.

Dagger2 Examples.

There are two examples: one in Java then one in Kotlin.

1. Hello World Dagger2 Example with SharedPreferences.

In this tutorial we want to introduce Dagger2 practically with a real world project. We will use the SharedPreferences to:

  1. Save Data From Edittext to SharedPreferences.
  2. Retrieve Data From SharedPreferences onto an EditText.

Our user interface comprises two edittexts where the user enters a username and his number and then clicks the save button. Then we get the value when the user clicks a get button. We rebind the value in the edittext.

Let’s start.

Here’s the demo:

Android Dagger2 Example

(a). Build.gradle

We start by proceeding to our app level build.gradle and adding our dependencies. Dagger2 is a third party library maintained by Google. We have to fetch it from internet so we start by adding it’s dependency via Gradle.

  1. Once you’ve created your android application in android studio, navigate over to the build.gradle located in the app folder and move to the dependencies DSL.
  2. Replace them with the following code. You can replace the versions with the latest versions.
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.google.dagger:dagger:2.16'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
}

Here are the dependencies we’ve added:

  1. appcompat- A support library which will give us access to the AppCompatActivity class.
  2. constraint-layout – To give us the constraint layout.
  3. dagger2 – Our third party dependency injection framework. We also add our annotationProcessor. Remember Dagger2 is annotation based framework and we will be using several annotations to annotate our classes, interfaces, methods and fields.
(b). activity_main.xml

Next we come to activity_main.xml. This layout typically gets inflated into the MainActivity. In this layout we will construct our user interface using several elements all which are indirect or direct subclasses of the View class.

They include

  1. LinearLayout– This will allow us arrange items linearly. In this case it is our container layout and we arrange it’s children vertically.
  2. TextView – Just to display our header.
  3. ImageView – To render our dummy image.
  4. EditText – To provide an interface for users to type data to be saved into our SharedPreferences.
  5. Button – When clicked we will either save to SharedPreferences or retrieve from them.

Here’s the full code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

    android_layout_width="match_parent"
    android_layout_height="match_parent"
    android_orientation="vertical"
    android_background="#009688"
    tools_context=".MainActivity">

    <TextView
        android_id="@+id/headerTxt"
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_text="Spiritual Teachers App"
        android_textAlignment="center"
        android_fontFamily="casual"
        android_textStyle="bold"
        android_textAppearance="@style/TextAppearance.AppCompat.Large"
        android_textColor="@color/white" />

    <ImageView
        android_layout_width="match_parent"
        android_layout_height="300dp"
        android_layout_centerHorizontal="true"
        android_src="@drawable/logo"
        android_contentDescription="@string/app_name"/>

    <RelativeLayout
        android_layout_width="match_parent"
        android_layout_height="wrap_content">

    <EditText
        android_id="@+id/usernameTxt"
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_layout_margin="8dp"
        android_textColor="@color/white"
        android_hint="Username"
        />

    <EditText
        android_id="@+id/numberTxt"
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_layout_below="@+id/usernameTxt"
        android_layout_margin="8dp"
        android_inputType="number"
        android_textColor="@color/white"
        android_hint="Number"
        />

    <Button
        android_id="@+id/saveBtn"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_text="SAVE"
        android_layout_below="@+id/numberTxt"
        android_layout_toLeftOf="@+id/getBtn"
        android_layout_toStartOf="@+id/getBtn"
        android_layout_marginRight="8dp"
        android_layout_marginEnd="8dp"
        />

    <Button
        android_id="@+id/getBtn"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_text="GET"
        android_layout_below="@+id/numberTxt"
        android_layout_alignRight="@+id/numberTxt"
        android_layout_alignEnd="@+id/numberTxt"
        />
    </RelativeLayout>

</LinearLayout>
(d). SharedPrefModule.java

This is our module class. Dagger2 has two main concepts, a module and a component. Basicly the module represents our dependencies while component represents a mapping between the module and it’s client/dependent object.

So we come create a module class here. But first we have to start by importing several objects:

  1. Context – required by our SharedPreferences class.
  2. SharedPreferences – We will return it’s instance as a dependency to the dependant.
  3. PreferenceManager – Will provide us our SharedPreference object.
  4. Singleton – will mark our object as a singleton. A singleton is a shared object. You can have one instance of a singleton object in memory.
  5. Module – will mark our SharedPrefModule class a module.
  6. Provides – Will mark our provider methods.

Here are the methods we create:

  1. SharedPrefModule(Context context) : Our constructor. Will take a Context object and assign it to a local instance field.
  2. provideContext() – a method that wil return us a Context object if needed as a dependency.
  3. provideSharedPreferences() – a method that will return us a SharedPreferences object as a dependency to the dependant.

Here’s the full code. Take not that we mark our methods with Singleton and Provides annotations.

package info.camposha.mrdagger;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

/**
 * Let's create our module. This will be mapped to our activity by the Component.
 * We will pass it a Context object via the constructor. Then it will provide that
 * Context as well as the SharedPreferences as our dependencies.
 */
@Module
public class SharedPrefModule {
    private Context context;

    public SharedPrefModule(Context context) {
        this.context = context;
    }

    @Singleton
    @Provides
    public Context provideContext() {
        return context;
    }

    @Singleton
    @Provides
    public SharedPreferences provideSharedPreferences(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context);
    }
}
(d). MyComponent.java

This is our Dagger Component interface. A Component is a class that provides the mapping between a Module and a Dependent object. In this case will be mapping our SharedPrefModule to our MainActivity. So MainActivity is the dependnat object. It depends on the Module which is the SharedPrefModule. From it can get two methods injected into it as dependencies. Those are methods marked with @Provides annotation.

Here are some of our imports:

  1. Component – This is an annotation that will mark our interface as a daggger component.

Here are the annotations used:

  1. @Singleton – To mark our interface implementation object as a singleton. Only a single instance of that object can exist in memory.
  2. @Component – To mark our interface as a component. We provide it our modules/module. In this case we have only a single module. Suppose we had many we would have separated them with commas.

Here are our methods:

  1. void inject(..) – Will receive our dependant and map it to our module.

Here’s the full code:

package info.camposha.mrdagger;

import javax.inject.Singleton;
import dagger.Component;

/**
 * Create our DaggerComponent. It is a singleton.
 * It will map our dependency which is our module to our dependent which is the
 * <code>MainActivity</code>.
 */
@Singleton
@Component(modules = {SharedPrefModule.class})
public interface MyComponent {
    void inject(MainActivity activity);
}
(e). MainActivity.java

This is our MainActivity class. This will be our launcher activity. It is registered on the android manifest as it is an android component. Here are the things we will do here:

  1. Set our contentview for this activity.
  2. Initialize widgets like Buttons and Edittexts.
  3. Get data from edittext and insert them into shared preference.
  4. Retrieve data from sharedpreferences and bind them to edittexts.
  5. Initialize our DaggerComponent and pass it this activity’s instance.
[notice] Rebuild you project so that Dagger2 can generate us the DaggerMyComponent class. [/notice]

That DaggerMyComponent is our MyComponent implementation. We will build it and pass it our module. Then we also inject into it our dependant.

Building The Component

Here’s the method responsible for that:

    private void initializeDaggerComponent(){
        myComponent = DaggerMyComponent
                .builder()
                .sharedPrefModule(new SharedPrefModule(this))
                .build();
        myComponent.inject(this);
    }

How to save to SharedPreferences

Here’s the method for that

    private void saveToSharedPreference(){
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("username", usernameTxt.getText().toString().trim());
        editor.putString("number", numberTxt.getText().toString().trim());
        editor.apply();
    }
package info.camposha.mrdagger;

import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import javax.inject.Inject;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    EditText usernameTxt, numberTxt;
    Button saveBtn, getBtn;
    private MyComponent myComponent;
    @Inject
    SharedPreferences sharedPreferences;

    /**
     * Initialize DaggerComponent and inject it our activity
     */
    private void initializeDaggerComponent(){
        myComponent = DaggerMyComponent
                .builder()
                .sharedPrefModule(new SharedPrefModule(this))
                .build();
        myComponent.inject(this);
    }

    /**
     * Initialize Buttons and EditTexts
     */
    private void initializeViews() {
        getBtn      = findViewById(R.id.getBtn);
        saveBtn     = findViewById(R.id.saveBtn);
        usernameTxt  = findViewById(R.id.usernameTxt);
        numberTxt    = findViewById(R.id.numberTxt);

        saveBtn.setOnClickListener(this);
        getBtn.setOnClickListener(this);
    }

    /**
     * Save to shared preference.
     * <code>SharedPreferences.Editor</code> is an interface used for modifying values in a
     * SharedPreferences object.
     * It's putString() will Set a String value in the preferences editor, to be
     * written back once commit() or apply() are called.
     */
    private void saveToSharedPreference(){
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("username", usernameTxt.getText().toString().trim());
        editor.putString("number", numberTxt.getText().toString().trim());
        editor.apply();
    }

    /**
     * Retrieve From SharedPreferences and Bind to EditText
     */
    private void retrieveFromSharedPreferences(){
        usernameTxt.setText(sharedPreferences.getString("username", "Default"));
        numberTxt.setText(sharedPreferences.getString("number", "12345"));
    }

    /**
     * When the buttons are clicked
     * @param view
     */
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.getBtn:
                retrieveFromSharedPreferences();
                break;
            case R.id.saveBtn:
                saveToSharedPreference();
                break;
        }

    }
    /**
     * Our onCreate method
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initializeViews();
        initializeDaggerComponent();
    }
}

Example 2: Kotlin Android Dagger2 Example

Here’s another dead simple Dagger2 example written in Kotlin.

Step 1: Install Dagger2

Start by installing Dagger2 in your build.gradle, by specifying the dependency statement as follows:

    implementation 'com.google.dagger:dagger:2.24'
    kapt 'com.google.dagger:dagger-compiler:2.24'

At the top of the app/build.gradle do not forget to apply the kapt plugin:

apply plugin: 'kotlin-kapt'

Step 2: Design Layout

Add a TextView to your main XML layout:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.elyeproj.simplestappwithdagger2.MainActivity">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="48sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Step 3: Write Code

Go to your main activity and add imports as follows. You may use AndroidX:

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import dagger.Component
import kotlinx.android.synthetic.main.activity_main.*
import javax.inject.Inject

Create a Component known as MagicBox as an interface:

@Component
interface MagicBox {
    fun poke(app: MainActivity)
}

Then a class called info.Annotate the constructor with the @Inject property:

class Info @Inject constructor() {
    val text = "Hello Dagger 2"
}

Then use them as follows in the MainActivity:

class MainActivity : AppCompatActivity() {

    @Inject lateinit var info: Info

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerMagicBox.create().poke(this)
        text_view.text = info.text
    }
}

Here is the full code

MainActivity.kt

package com.elyeproj.simplestappwithdagger2

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import dagger.Component
import kotlinx.android.synthetic.main.activity_main.*
import javax.inject.Inject

class MainActivity : AppCompatActivity() {

    @Inject lateinit var info: Info

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerMagicBox.create().poke(this)
        text_view.text = info.text
    }
}

class Info @Inject constructor() {
    val text = "Hello Dagger 2"
}

@Component
interface MagicBox {
    fun poke(app: MainActivity)
}

Reference

Download the code here

More Examples

Here are more examples

1. Kotlin Android Dagger2 Single Module Example

A simple Dagger2 example involving a single module.

It is a simple application using Dagger2 code>@Component</code. It just defines a simple one code>@Module</code.

Step 1: Install Dagger2

In your dependencies add Dagger2 and Timber2 among your dependencies:

    implementation 'com.jakewharton.timber:timber:4.7.1'

    implementation 'com.google.dagger:dagger:2.21'
    kapt ' com.google.dagger: dagger-compiler: 2.21 '

Step 2: Design Layout

Design your xml layout as follows:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 3: Initialize Timber

Timber is our Logging library. Initialize inside our Application class:

App.kt

package com.star_zero.sample.dagger_tutorial.step1

import android.app.Application
import timber.log.Timber

class App : Application() {

    override fun onCreate() {
        super.onCreate()
        Timber.plant(Timber.DebugTree())
    }
}

Step 4: Create Repository classes

(a). UserRepository.kt

Create an Interface with one function that returns a String:

package com.star_zero.sample.dagger_tutorial.step1.data.repository

interface UserRepository {
    fun getName(): String
}

(b). UserDataRepository.kt

Implement the UserRepository aond override the getName() function:

package com.star_zero.sample.dagger_tutorial.step1.data.repository

class UserDataRepository(private val baseURL: String) : UserRepository {
    override fun getName(): String {
        return "Sample Name, baseURL=$baseURL"
    }
}

Step 5: Create App Module and Component:

(a). AppModule.kt

package com.star_zero.sample.dagger_tutorial.step1.di

import com.star_zero.sample.dagger_tutorial.step1.data.repository.UserDataRepository
import com.star_zero.sample.dagger_tutorial.step1.data.repository.UserRepository
import dagger.Module
import dagger.Provides

@Module
class AppModule(private val baseURL: String) {
    // (It is recommended to use @BindsInstance explained in step5 rather than the constructor argument of Module.)

    @Provides
    fun provideUserRepository(): UserRepository {
        return UserDataRepository(baseURL)
    }
}

(b). AppComponent.kt

package com.star_zero.sample.dagger_tutorial.step1.di

import com.star_zero.sample.dagger_tutorial.step1.ui.MainActivity
import dagger.Component

@Component(
    modules = [
        AppModule::class
    ]
)
interface AppComponent {
    // Create a method with the class that has the field you want to inject as an argument
    fun inject(activity: MainActivity)
}

Step 6: Create UI code

(a). MainViewModel.kt

package com.star_zero.sample.dagger_tutorial.step1.ui

import com.star_zero.sample.dagger_tutorial.step1.data.repository.UserRepository
import javax.inject.Inject

class MainViewModel @Inject constructor(
    private val userRepository: UserRepository
) {

    fun getName(): String {
        return userRepository.getName()
    }
}

(b). MainActivity.kt

package com.star_zero.sample.dagger_tutorial.step1.ui

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.star_zero.sample.dagger_tutorial.step1.R
import com.star_zero.sample.dagger_tutorial.step1.data.repository.UserRepository
import com.star_zero.sample.dagger_tutorial.step1.di.AppModule
import com.star_zero.sample.dagger_tutorial.step1.di.DaggerAppComponent
import timber.log.Timber
import javax.inject.Inject

class MainActivity : AppCompatActivity() {

    // Field injection
    @Inject
    lateinit var userRepository: UserRepository

    @Inject
    lateinit  var viewModel :  MainViewModel  // Since it is not an AAC ViewModel, you can create an instance normally.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Timber.d("onCreate")

        val appComponent = DaggerAppComponent.builder()
            .appModule ( AppModule ( " https://example.com " )) // If the Module has arguments, you need to create an instance and pass it.
            .build()
        appComponent.inject(this)

        Timber.d("userRepository.getName = ${userRepository.getName()}")
        Timber.d("viewModel.getName = ${viewModel.getName()}")
    }
}

Reference

  • Download full code here.
  • Follow code authorhere.
[Read Individually.](https://android.camposha.info/en/example/dagger-single-module/)