Dependency Injection with Dagger 2 in Android
If there are two classes, class A and class B and class A depends on class B then class B is called dependent for class A.
So, Every time we want to access class B in class A we need to create an instance of class B in Class A or use static factory methods to access class A. But this will make our code tight coupled, difficult to manage, and test. In order to remove these problems, we use dependency injection. Dependency Injection is a design pattern that removes the dependency from the programming code and makes the application easy to manage and test. It also makes programming code loosely coupled.

Dependency Injection in Android
Let us assume, we want to store some data in SharedPreferences. In order to save or retrieve shared preferences data, we need the instance of shared preference in our Activity's boilerplate code. And it may lead to problems in testing, managing, etc. if our codebase is large. This is where Android Dependency Injection helps. Here, SharedPreferences acts as a dependency for our Activity so, we don't create its instance in our activity rather we inject it from some other class.
Below is an illustration of the situation.

Dagger 2
Dagger 2 is a compile-time android dependency injection framework that uses Java Specification Request 330 and Annotations. Some of the basic annotations that are used in dagger 2 are:
- @Module This annotation is used over the class which is used to construct objects and provide the dependencies.
- @Provides This is used over the method in the module class that will return the object.
- @Inject This is used over the fields, constructor, or method and indicate that dependencies are requested.
- @Component This is used over a component interface which acts as a bridge between @Module and @Inject. (Module class doesn't provide dependency directly to requesting class, it uses component interface)
- @Singleton This is used to indicate only a single instance of dependency object is created.
Example
In this example, we will add some data to shared preferences and then retrieve it from there using the dagger 2 library. Below is the picture of what we are going to do in this example.
Note that we are going to implement this project using the Java language.

Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio.
Note that select Java as the programming language.
Step 2: Adding Dependencies
In order to use dependency injection with the help of dagger 2 libraries, we need to add it's dependency. Go to Gradle Scripts > build.gradle(Module: app) and add the following dependencies. After adding these dependencies you need to click on Sync Now.
dependencies { implementation ("com.google.dagger:dagger:2.45") annotationProcessor ("com.google.dagger:dagger-compiler:2.45") // For Kotlin kapt ("com.google.dagger:dagger-compiler:2.45")
kapt("androidx.room:room-compiler:2.6.1") }
For Kotlin:
plugins {
id 'kotlin-kapt'
}
Step 3: Working with the activity_main.xml file
In this step, we will create a layout file for the application. We have used EditText for taking the input from the user and a TextView for presenting the output and save and show buttons respectively. Below is the code snippet for the activity_main.xml file.
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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:background="@color/white"
tools:context=".MainActivity">
<Button
android:id="@+id/saveBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/inputField"
android:layout_centerInParent="true"
android:layout_margin="8dp"
android:backgroundTint="@android:color/holo_green_dark"
android:text="Save"
android:textColor="#ffffff" />
<Button
android:id="@+id/getBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/outputField"
android:layout_centerInParent="true"
android:layout_margin="8dp"
android:backgroundTint="@android:color/holo_green_dark"
android:text="Show"
android:textColor="#ffffff" />
<!--For input we have used EditText-->
<EditText
android:id="@+id/inputField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="40dp"
android:textSize="20sp"
android:hint="Input Data"
android:textAlignment="center" />
<!--For output we have used TextView-->
<TextView
android:id="@+id/outputField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/saveBtn"
android:textSize="20sp"
android:layout_margin="8dp"
android:hint="Output Data"
android:textAlignment="center" />
</RelativeLayout>
Layout Design:
Step 4: Creating Module Class
Now, we will create a Module class which is used to construct the object and provide the dependencies. @Module annotations are used over the module class. This class contains a constructor that will initialize the context and a method that will return the dependent object for which @Provides annotation is used. Here, provideSharedPreferences() method will return the dependent object. In general, the method that returns the dependent object will be followed by the word provide. Go to the app > java > package > right-click and create a new java class and name it as SharedPreferenceModule.
Below is the code snippet for the SharedPreferenceModule file.
package com.gfg.dependency_injection;
import android.content.Context;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
@Module
public class SharedPreferenceModule {
private Context context;
// Context gets initialize from the constructor itself
public SharedPreferenceModule(Context context) {
this.context = context;
}
@Singleton
@Provides
public Context provideContext() {
return context;
}
// @Singleton indicates that only single instance
// of dependency object is created
// @Provide annotations used over the methods that
// will provides the object of module class
// This method will return the dependent object
@Singleton
@Provides
public SharedPreferences provideSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
}
package com.gfg.dagger_kotlin
import android.content.Context
import android.content.SharedPreferences
import android.preference.PreferenceManager
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@Module
class SharedPreferenceModule(private val context: Context) {
@Singleton
@Provides
fun provideContext(): Context {
return context
}
@Singleton
@Provides
fun provideSharedPreferences(context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
}
}
Step 5: Creating a Component Interface
In this step, we will create an Interface. Go to the app > java > package > right-click and create an interface and name it as SharedPreferenceComponent. We use @Component annotation in order to mention all the modules.
@Component(modules={SharedPreferenceModule})
The Activities, Fragments, or Services that may request the dependencies declared by modules must be declared in this interface with the individual inject() method. Below is the code snippet for the SharedPreferenceComponent Interface.
package com.gfg.dependency_injection;
import javax.inject.Singleton;
import dagger.Component;
// All the modules are mentioned under
// the @Component annotation
@Singleton
@Component(modules = {SharedPreferenceModule.class})
public interface SharedPreferenceComponent {
void inject(MainActivity mainActivity);
}
package com.gfg.dagger_kotlin
import dagger.Component
import javax.inject.Singleton
// All the modules are mentioned under
// the @Component annotation
@Singleton
@Component(modules = [SharedPreferenceModule::class])
interface SharedPreferenceComponent {
fun inject(mainActivity: MainActivity?)
}
Step 6: Working With the MainActivity.java File
In this step, we will first initialize our Views and then bind Dagger to our application. For which component-interface is followed by the Dagger keyword.
sharedPreferenceComponent = DaggerSharedPreferenceComponent.builder().sharedPreferenceModule(new SharedPreferenceModule(this)).build();
sharedPreferenceComponent.inject(this);
Below is the code snippet for the MainActivity file.
Note: When you will use Dagger as a prefix with Component(here, SharedPreferenceComponent) sometimes you may get an error or warning this is because DaggerSharedPreferenceComponent is generated after compilation.
package com.gfg.dependency_injection;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
EditText editText;
TextView textView;
Button saveBtn, getBtn;
private SharedPreferenceComponent sharedPreferenceComponent;
// @Inject is used to tell which activity,
// fragment or service is allowed to request
// dependencies declared in Module class
@Inject
SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Referencing the EditText, TextView and Buttons
editText = (EditText) findViewById(R.id.inputField);
textView = (TextView) findViewById(R.id.outputField);
saveBtn = (Button) findViewById(R.id.saveBtn);
getBtn = (Button) findViewById(R.id.getBtn);
// Setting onClickListener behavior on button to reference
// to the current activity(this MainActivity)
saveBtn.setOnClickListener(this::onClick);
getBtn.setOnClickListener(this::onClick);
// Here we are binding dagger to our application
// Dagger keyword will be prefix to the component name
sharedPreferenceComponent = DaggerSharedPreferenceComponent.builder().sharedPreferenceModule(
new SharedPreferenceModule(this)).build();
// we are injecting the shared preference dependent object
sharedPreferenceComponent.inject(this);
}
public void onClick(View view) {
if(view.getId()==R.id.saveBtn) {
// Saving data to shared preference
// inputField acts as key and editText data as value to that key
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("inputField", editText.getText().toString().trim());
editor.apply();
}
else if(view.getId()==R.id.getBtn){
// getting shared preferences data and set it to textview
// s1: is the default string, You can write any thing there or leave it
textView.setText(sharedPreferences.getString("inputField", ""));
}
}
}
package com.gfg.dagger_kotlin
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
private lateinit var editText: EditText
private lateinit var textView: TextView
private lateinit var saveBtn: Button
private lateinit var getBtn: Button
private lateinit var sharedPreferenceComponent: SharedPreferenceComponent
@Inject
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editText = findViewById(R.id.inputField)
textView = findViewById(R.id.outputField)
saveBtn = findViewById(R.id.saveBtn)
getBtn = findViewById(R.id.getBtn)
saveBtn.setOnClickListener(::onClick)
getBtn.setOnClickListener(::onClick)
sharedPreferenceComponent = DaggerSharedPreferenceComponent.builder()
.sharedPreferenceModule(SharedPreferenceModule(this))
.build()
sharedPreferenceComponent.inject(this)
}
private fun onClick(view: View) {
when (view.id) {
R.id.saveBtn -> {
val editor = sharedPreferences.edit()
editor.putString("inputField", editText.text.toString().trim())
editor.apply()
}
R.id.getBtn -> {
textView.text = sharedPreferences.getString("inputField", "")
}
}
}
}