Android Dependency Injection – Dagger 2

Filed Under: Android

In this tutorial, we’ll discuss and implement Dependency Injection (DI) in our android application. For those of you who have used DI in the past (possibly in Spring), the following section can be skipped about introduction of Android Dependency Injection.

What is Android Dependency Injection?

You don’t create objects yourself, you let someone else create them for you.

Let’s analyze what the above statement means.

Imagine the following scenario: We have an Activity and we wish to save some data in the SharedPreferences. Doing this without DI would lead us to instantiate, saving, retrieving data from the SharedPreferences, all within the Activity’s boilerplate code. Something that the below illustration more or less portrays to an extent.

android no dependency injection

Without DI

The above approach can lead to some serious issues especially when your codebase increases in size.

You’ll have issues with unit testing, too much boilerplate code, difficulties in modifying your current codebase with so many instances to keep an eye on (shared instances, scoped instances). This is where Android Dependency Injection Pattern can give a major boost to your code.

As mentioned in the dependency and with the following illustration, instead of instantiating the SharedPreferences in our Activity every time, we’d let it be injected from another class.

android dependency injection

With DI

So our Activity is dependent on the SharedPreferences. Thus SharedPreferences acts as a dependency for our Activity. SharedPreferences gets injected into our Activity from the outside instead of getting instantiated.

Why use Android Dependency Injection?

If you’re the boss of a company, would you prefer doing all the things yourself or delegate the tasks?

Would you prefer hiring a rookie over an expert who already knows the task?

In an ideal situation, the answer to both of the above questions should be the latter option.

That’s why Dependency Injection is so critical. Instead of instantiating SharedPreferences, Databases, Network Libraries in our Activity each time, we’ll prefer to have instances of them created somewhere else and inject them in our Activity when it’s needed.

These dependencies are normally used in our classes using constructors that we’ll be seeing soon.

What is Dagger 2?

Dagger library was created by developers at Square, way back in 2012. Dagger 1 was used to create instances of classes and inject dependencies via Reflections. Improving upon the first version, and collaborating with a team of developers at Google, Dagger 2 a much faster and improved version without Reflections was introduced.

Dagger 2 is a compile-time android dependency injection framework and uses the Java Specification Request (JSR) 330 and uses an annotation processor.

Following are the basic annotations used in Dagger 2:

  1. @Module : This is used on the class that does the work of constructing objects that’ll be eventually provided as dependencies.
  2. @Provides : This is used on the methods inside the Module class that’ll return the object.
  3. @Inject : This is used upon a constructor, field or a method and indicates that dependency has been requested.
  4. @Component : The Module class doesn’t provide the dependency directly to the class that’s requesting it. For this, a Component interface is used that acts as a bridge between @Module and @Inject.
  5. @Singleton : This indicates that only a single instance of the dependency object would be created.

Don’t worry if the above stuff went over your head. We’ll now develop an application that uses Dagger and provides SharedPreferences as the dependency in our Activity.

Android Dependency Injection Example Project

android dependency injection example, android dagger 2

Our application would consist of a Single Activity wherein we’ll save and retrieve the data from our EditTexts into our SharedPreferences.

First and foremost, add the following dependencies in the build.gradle file.


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

Let’s analyze how we’ll use Dagger and the above-mentioned annotations to bring in the power of Dependency Injection in our application.
android dagger 2 workflow

To instantiate the SharedPreferences instance in another class, we need to pass the current context.
The Component does the work of passing the stuff to the Module class and Injects the dependency in our Activity.

Android Dagger 2 Code

Let’s look at the activity_main.xml layout first.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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.journaldev.dagger2.MainActivity">

    <EditText
        android:id="@+id/inUsername"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:hint="Username" />

    <EditText
        android:id="@+id/inNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/inUsername"
        android:layout_margin="8dp"
        android:inputType="number"
        android:hint="Number" />

    <Button
        android:id="@+id/btnSave"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SAVE"
        android:layout_below="@+id/inNumber"
        android:layout_toLeftOf="@+id/btnGet"
        android:layout_toStartOf="@+id/btnGet"
        android:layout_marginRight="8dp"
        android:layout_marginEnd="8dp" />

    <Button
        android:id="@+id/btnGet"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GET"
        android:layout_below="@+id/inNumber"
        android:layout_alignRight="@+id/inNumber"
        android:layout_alignEnd="@+id/inNumber" />

</RelativeLayout>

The code for the SharedPrefModule.java class is given below.


package com.journaldev.dagger2;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;

@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);
    }
}

The context is set in the constructor itself. Methods that return the dependent object are typically prepended with provide, hence the name provideSharedPreferences().

The code for the MyComponent.java interface is given below.


package com.journaldev.dagger2;

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

@Singleton
@Component(modules = {SharedPrefModule.class})
public interface MyComponent {
    void inject(MainActivity activity);
}

All the modules are mentioned inside the @Component annotation.
The activities, services, or fragments that are allowed to request the dependencies declared by the modules (using @Inject) should be declared in this interface with individual inject() methods.

The code for the MainActivity.java class is given below:


package com.journaldev.dagger2;

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 inUsername, inNumber;
    Button btnSave, btnGet;
    private MyComponent myComponent;
    @Inject
    SharedPreferences sharedPreferences;

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

        initViews();
        myComponent = DaggerMyComponent.builder().sharedPrefModule(new SharedPrefModule(this)).build();
        myComponent.inject(this);


    }

    private void initViews() {
        btnGet = findViewById(R.id.btnGet);
        btnSave = findViewById(R.id.btnSave);
        inUsername = findViewById(R.id.inUsername);
        inNumber = findViewById(R.id.inNumber);
        btnSave.setOnClickListener(this);
        btnGet.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {

            case R.id.btnGet:
                inUsername.setText(sharedPreferences.getString("username", "default"));
                inNumber.setText(sharedPreferences.getString("number", "12345"));
                break;
            case R.id.btnSave:
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putString("username", inUsername.getText().toString().trim());
                editor.putString("number", inNumber.getText().toString().trim());
                editor.apply();
                break;

        }
    }
}

The code to bind Dagger into our application is:


myComponent = DaggerMyComponent.builder().sharedPrefModule(new SharedPrefModule(this)).build();

Dagger keyword is prepended on the Component class name. If the component class name was AppComponent, the result would have been DaggerAppComponent.

The Module class name is converted into camel case format and the Module is initialized inside it by passing the context(which is eventually required by the SharedPreferences to initialize!).

At this stage Dagger can generate files, so you need to Rebuild your project to allow it to do its job (Build > Rebuild Project).

The SharedPreferences dependency object is available to use after this: myComponent.inject(this).

We are able to operate upon our SharedPreferences, without the need to initialize it within the Activity.

We can do the same with Retrofit adapters etc to make our code simpler and easier to use/test.

The output of the above application is given below:
android dagger 2, android dependency injection example

Instead of @Singleton, we can define different Scopes too, such as @PerApp, @PerActivity to let the number of instance creations depend upon the lifetime of Application/Activity respectively.

This brings an end to android dependency injection tutorial. There’s a lot more to Dagger 2 that we’ll discuss in later tutorials. You can download the final Android Dagger2 Project from the link below.

Reference: Dagger Docs

Comments

  1. sohan patki says:

    DaggerMyComponent is not getting identified in my IDE.

    1. kalpesh Borane says:

      Hii dear…kindly add @Singlenton annotation in Mycomponent module

  2. farhana says:

    I have learned so many blogs and post about dagger but was unable to understand correctly but your explanation with example is simple and understandable… thanks

  3. Ram says:

    Hi
    This is the Simplest example which explains Dagger 2 clearly.
    Thank you for this article.

    but i have a small doubt while initializing mycomponent we are creating object of “new SharedPrefModule(this)”.
    then what is the advantage of Dagger. we can use that object directly right.
    can you please clarify my doubt.
    myComponent = DaggerMyComponent.builder().sharedPrefModule(new SharedPrefModule(this)).build();

    1. Ranjeet Chouhan says:

      Hey, Ram

      As per your doubt hereby I’m explaining this to you. One thing you noticed he used @Singleton annotation in SharedPreference module’s method which is annoted @Provides so, dagger will create only a single object and if you want to use SharedPreference instance in multiple activity then that instance is shared within all of your activity with creating new instance into JVM and also if you want to do unitTest of your code than you can easily achieve it.

  4. John says:

    2. @Provide (Should be “@Proivdes”)

    “Dagger keyword is appended on the Component class name” – The word is “prepended”, not “appended”.

    1. Pankaj says:

      Thanks for such a close review, I have fixed the typo errors.

  5. Luke Loze says:

    DI (also known as IOC) is not the same as an IOC container/DI framework.

    Spring/Dagger 2 are IOC containers, they ‘help’ you do DI.
    You can do DI/IOC without a framework, the standard way is to inject dependencies in as constructor arguments.

    IOC Containers can be very useful, though they can lead to bad practice. I spent years autowiring private fields, then having to use reflection to unit test, should have autowired constructor arguments instead.

    The singleton (anit-) pattern should be avoided, it introduces global state/variables to your application.

    IOC containers are also black box ‘magic’, when they go wrong the debugging can take a lot more time than creating the dependency graph by hand.

Leave a Reply

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

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages