Android Dagger 2: Critical things to know before you implement.

Dagger in a Dependency Injection framework for Android. I assume the reader is familiar with Dagger and it’s usage pattern in Android for the sake of this Article.
I strongly recommend the reader to go through the two part series Article by me on Dagger.
- Introduction to Dagger 2, Using Dependency Injection in Android: Part 1
- Introduction to Dagger 2, Using Dependency Injection in Android: Part 2
Remember this is a complex topic so get involved actively and analyse the code with a focused mind.
We will analyze Dagger using the project I had created for Part 2 of the above Article series. The project link in mentioned below.
https://github.com/MindorksOpenSource/android-dagger2-example
What are we going to study in the following analysis?
We will study when and how a dependency class is instantiated? And take into account the scenarios that may produce unexpected results.
Let’s understand how we create a singleton class?
A Singleton class only exists with a single instance for the entire application. In Dagger, we create a singleton class by annotating it with @Singleton
annotation. This works well when we instantiate the class using constructor injection. But fails when we provide that class using @provides
in a module under specific scenarios described below.
In the above-mentioned git repository we will change few classes and test for the singleton class.
Using @Singleton
on a class and providing it using new
keyword in the @provides
annotated method of a module.
Create a package sample and add a class DependencySample1 in it.

@Singleton
public class DependencySample1 {
private static final String TAG = "DependencySample1";
private int value;
public DependencySample1(int value) {
this.value = value;
Log.d(TAG, "DependencySample1: " + this.hashCode());
}
}
In DataManager
class express its dependency in the constructor.
@Singleton
public class DataManager {
...
@Inject
public DataManager(@ApplicationContext Context context,
DbHelper dbHelper,
SharedPrefsHelper sharedPrefsHelper,
DependencySample1 dependencySample1Instance) {
mContext = context;
mDbHelper = dbHelper;
mSharedPrefsHelper = sharedPrefsHelper;
mDependencySample1Instance1 = dependencySample1Instance;
}
...
}
Also, in DemoApplication
class express its dependency.
public class DemoApplication extends Application {
...
@Inject
DependencySample1 dependencySample1Instance1;
...
}
In ApplicationModule
provide the dependency of this class.
@Module
public class ApplicationModule {
...
@Provides
DependencySample1 provideDependencySample1() {
Log.d(TAG, "provideDependencySample1: provideDependencySample1 called");
return new DependencySample1(3);
}
}
After runing the app, we observe the following.
Logs reveal that two instances of the DependencySample1
class are created even when the class is annotated with @Singleton
.
- 01–12 08:09:25.389 D/ApplicationModule: provideDependencySample1: provideDependencySample1 called
- 01–12 08:09:25.390 D/DependencySample1: DependencySample1: 746992865
- 01–12 08:09:25.390 D/DataManager: DependencySample1: 746992865
- 01–12 08:09:25.390 D/ApplicationModule: provideDependencySample1: provideDependencySample1 called
- 01–12 08:09:25.390 D/DependencySample1: DependencySample1: 541540870
- 01–12 08:09:25.390 D/DemoApplication: DependencySample1: 541540870
Heap Dump confirmation:


Since we used @Singleton
on DependencySample1
class, we expected to get the same object reference in both the places but we got two different objects. So, how can we solve the above problem?
Now let’s modify the @Provides
method in the ApplicationModule
class by adding @Singleton
on the provide method for DependencySample1
.
@Module
public class ApplicationModule {
...
@Provides
@Singleton
DependencySample1 provideDependencySample1() {
Log.d(TAG, "provideDependencySample1: provideDependencySample1 called");
return new DependencySample1(3);
}
...
}
After running the application with the above modification we observe the following.
Logs reveal that this time only one instance is created and shared in both DataManager
and DemoApplication
class.
- 01–12 08:07:41.004 D/ApplicationModule: provideDependencySample1: provideDependencySample1 called
- 01–12 08:07:41.006 D/DependencySample1: DependencySample1: 186196167
- 01–12 08:07:41.006 D/DataManager: DependencySample1: 186196167
- 01–12 08:07:41.006 D/DemoApplication: DependencySample1: 186196167
Heap Dump confirmation:


Note: In the above example we don’t need to annotate the DependencySample1
class with @Singleton
when we are annotating the provide method with @Singleton
.
So, if we need to make a class singleton and we provide it withnew
keyword then annotate the method that provides it in the module with@Singleton
in place of putting it on the class.
In cases where we are providing the dependency of a class and the class is able to construct itself from the existing dependencies in the graph. We will get a singleton class by annotating that class with @Singleton
i.e. we are not using new
keyword in the provides method.
Let’s try understand what I mean by the above statement.
Modify the DependencySample1
class with the below code.
@Singleton
public class DependencySample1 {
private static final String TAG = "DependencySample1";
private int value;
@Inject
public DependencySample1(@Named(value = "DependencySample1_Integer") Integer value) {
this.value = value;
Log.d(TAG, "DependencySample1: " + this.hashCode());
}
}
Here we are providing the dependency of DependencySample1
through constructor injection. @Named
is used to help Dagger resolve Integer
type dependency, so to avoid any conflict. We have used Integer Type dependency just for simplicity.
Also, modify the ApplicationModule
provide method.
@Module
public class ApplicationModule {
...
@Provides
@Named(value = "DependencySample1_Integer")
Integer provideDependencySample1Integer() {
Log.d(TAG, "provideDependencySample1: provideDependencySample1Integer called");
return 3;
}
...
}
This time we are providing the Integer dependency that will construct the DependencySample1
through constructor injection. As in the previous case, here also DependencySample1
is injected in DemoApplication
and DataManager
class.
After running the application with the above modification we observe the following.
Logs reveal that we get a singleton DependencySample1
class.
- 01–12 08:34:28.022 D/ApplicationModule: provideDependencySample1: provideDependencySample1Integer called
- 01–12 08:34:28.022 D/DependencySample1: DependencySample1: 746992865
- 01–12 08:34:28.022 D/DataManager: DependencySample1: 746992865
- 01–12 08:34:28.022 D/DemoApplication: DependencySample1: 746992865
Heap Dump confirmation:


Note
If we remove @Singleton
from the DependencySample1
we get the following logs. Confirming that each class get different instance of DependencySample1
class.
- 01–12 08:35:35.385 D/ApplicationModule: provideDependencySample1: provideDependencySample1Integer called
- 01–12 08:35:35.386 D/DependencySample1: DependencySample1: 746992865
- 01–12 08:35:35.386 D/DataManager: DependencySample1: 746992865
- 01–12 08:35:35.386 D/ApplicationModule: provideDependencySample1: provideDependencySample1Integer called
- 01–12 08:35:35.386 D/DependencySample1: DependencySample1: 541540870
- 01–12 08:35:35.386 D/DemoApplication: DependencySample1: 541540870
What happens when we mention a class in a get method in a component class interface and don’t inject it anywhere?
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
...
DependencySample1 getDependencySample1();
}
In this case, the DependencySample1
is not instantiated till it is accessed via the component interface method.
- Case 1:
getDependencySample1()
is never called i.e.applicationComponent.getDependencySample1()
is not used. Also,DependencySample1
is not injected i.e.@Inject
is not used onDependencySample1
in any class. In this case,DependencySample1
do not get instantiated.
Heap Dump confirmation:

- Case 2:
getDependencySample1()
is called i.e.applicationComponent.getDependencySample1()
is used andDependencySample1
is not injected i.e.@Inject
is not used onDependencySample1
in any class. In this case theDependencySample1
is instantiated whengetDependencySample1()
is called. All the rules of singleton we witnessed above are applicable here.
For the above test we modify the DemoApplication
class to access the getDependencySample1()
.
public class DemoApplication extends Application {
...
private DependencySample1 dependencySample1;
@Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
dependencySample1 = applicationComponent.getDependencySample1();
}
...
}
Logs reveal that this time DependencySample1
was instantiated.
- 01–12 05:09:56.670 D/ApplicationModule: provideDependencySample1: provideDependencySample1Integer called
- 01–12 05:09:56.670 D/DependencySample1: DependencySample1: 324132341
Heap Dump confirmation:


Heap dump reveals that one instance of DependencySample1
was created.
We now have understood the various aspects of Dagger with singleton classes. Now let’s focus on Scope to understand how and what happens in a scoped variable?
Scope creates the instance of a class that has the same rules as the Singleton but the difference is that Singleton creates the global single instance and Scope creates the single instance in that scope.
In the above project, I have created a custom scope @PerActivity
to provide dependencies for each Activity.
Let’s modify the ActivityModule
class to provide the DependencySample1
in the same fashion as we did with the ApplicationModule
.
@Module
public class ActivityModule {
...
@Provides
DependencySample1 provideDependencySample1() {
Log.d(TAG, "provideDependencySample1: called");
return new DependencySample1(3);
}
}
Modify DependencySample1
class by annotating with @PerActivity
.
@PerActivity
public class DependencySample1 {
private static final String TAG = "DependencySample1";
private int value;
public DependencySample1(int value) {
this.value = value;
Log.d(TAG, "DependencySample1: " + this.hashCode());
}
}
Modify MainActivity
class to inject the DependencySample1
twice.
public class MainActivity extends AppCompatActivity {
...
@Inject
DependencySample1 dependencySample1;
@Inject
DependencySample1 dependencySample2;
...
}
Logs reveal that two instances of the DependencySample1
class is formed if we use new
keyword, which is similar to what we found with ApplicationModule
.
- 01–12 04:35:57.697 D/ActivityModule: provideDependencySample1: called
- 01–12 04:35:57.699 D/DependencySample1: DependencySample1: 871533216
- 01–12 04:35:57.699 D/ActivityModule: provideDependencySample1: called
- 01–12 04:35:57.699 D/DependencySample1: DependencySample1: 474084953
Heap Dump confirmation:


When we add @PerActivity
to the provide method then we will get the single instance of DependencySample1
. Similar to what we found with @Singleton
.
@Module
public class ActivityModule {
...
@Provides
@PerActivity
DependencySample1 provideDependencySample1() {
Log.d(TAG, "provideDependencySample1: called");
return new DependencySample1(3);
}
...
}
All other rules are valid as with the @Singleton
, only that it is associated with each Activity. Each Activity creates new set of dependencies.
The above study reveals some of the aspects of the Dagger which must be understood clearly. It can create disasters if not used correctly.
Let’s become friends on Twitter, Linkedin, Github, and Facebook.
Learning is a journey, let’s learn together!