Dagger-Android-Part 2

Dheeraj Andra
MindOrks
Published in
4 min readApr 27, 2019

Welcome to the part 2 of this Dagger Series. In the previous part we have understood what Dependency Injection in Software Development is and what Dagger is used for.

If you haven’t already gone through the first part of the series, I would request you to give a look here for better understanding of Dependency Injection.

In this part of the series, we are going to discuss what is Annotation processing and different kinds of annotation we use in Dagger like Scope and Qualifier.

More often, when we use Dagger in our Android Application Development, we see some classes containing @Module, @Inject, @Provides, @Scope, @Qualifier , etc., What is the common thing that we observe among all these terms? The Symbol “@”. These are called annotations and help us very much in avoiding repetitive code.

So, let’s understand what annotations are and how does annotation processing help us in better coding by observing the @Override annotation.

We have come across this @Override annotation many a time whenever we extend an abstract class or an interface. Implementing the abstract methods in our custom class implies that we are overriding the respective method that is defined in the parent class.

Let’s take a quick and simple example. Let’s create a base class Car. And let this base class be extended by a custom class. Let’s call the custom class BMW. BMW uses the common functionalities that are present in the parent Car class, but some of the properties may differ. Let’s say there is a method calculateTopSpeed() in the parent Car class. The definition of this method will not be the same for BMW and hence it overrides that method and gives its own definition.

In the source code for @override , it is mentioned that the method on which this annotation is used overrides or implements a method that is declared in the super type.

package java.lang;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

To learn more on annotations, please refer to this blog:

Whenever we use annotations, try understanding the basic definition of the respective annotation by referring the source code. We will be understanding the main annotations(Module, Component, Inject and Provides) that are being used in Dagger in a much better way in our next part of the series. For now I hope you have understood the basic definition of annotations being used in Android Development.

Let’s understand the annotations @Qualifier and @Scope in this part of the series.

Qualifiers help in differentiation of the objects(of the same type) that are being injected. Fore more information and better understanding of Qualifiers, please refer to this blog :

Now, let’s understand @Scope annotation. We will be using this in our code that we are going to develop in Part 4 and Part 5 of the series. But for now, lets understand @Scope with a simple example provided in the following blog:

Let’s consider the code snippet provided in the mentioned blog:

Firstly, let’s define our own scope(Application Scope). We create a custom scope by using @Scope annotation.

@Scope
@Retention
annotation class ApplicationScope

Now, let’s create an unscoped component to understand the power of @Scope

data class Warrior(val name: String)

@Component(modules = [AppModule::class])
interface AppComponent {
fun getWarrior(): Warrior
}

@Module
class AppModule {
private var index = 0

@Provides
fun provideWarrior(): Warrior {
index++
return Warrior("Warrior $index")
}
}

We can see that there is no scope that is provided here. Now, let’s define a data class Warrior with a name property. This will serve as our dependency. Next we define a module that provides this dependency. But every time a new warrior is instantiated, we increment the index. This will help us to figure out if indeed a new instance is created every time. Finally a component that exposes this dependency. Then let us instantiate in our application.

Don’t be confused with the Dagger annotations that are being used in the code snippet. We will be covering them in our next part of the series.

class WarriorApplication : Application() {
private val TAG = "WarriorApplication"

override fun onCreate() {
super.onCreate()
val appComponent = DaggerAppComponent.builder()
.appModule(AppModule())
.build()
Log.d(TAG, appComponent.getWarrior().name)
Log.d(TAG, appComponent.getWarrior().name)
}
}

Running the app will output these logs:

D/WarriorApplication: Warrior 1
D/WarriorApplication: Warrior 2

We can see that two different instances are created. This is expected as the component and dependency is unscoped. Now let us annotate our component and provide method with our new scope.

@ApplicationScope
@Component(modules = [AppModule::class])
interface AppComponent {
fun getWarrior(): Warrior
}

@Module
class AppModule {
private var index = 0

@ApplicationScope
@Provides
fun provideWarrior(): Warrior {
index++
return Warrior("Warrior $index")
}
}

Running the application will now output the following logs

D/WarriorApplication: Warrior 1
D/WarriorApplication: Warrior 1

Notice that the same instance is returned to us. This is the power of scoping. It provides us local singletons within the defined scope. Now, the scope is only valid within the component. When a new component of the same type is initialised, it will have a new sets of dependencies. This is the reason why @Singleton components are instantiated and maintained in the Application class. The singleton application component should persist throughout the application lifecycle, and the app should refer to this component to uphold that “single instance” requirement.

Congratulations on understanding the basic introduction to annotation processing, @Qualifier and @Scope.

We will be understanding the rest of the annotations used in Dagger in our next part of the series.

That’s it for part 2 of this series. Thank you for your time.

You can refer the Part 3 of the series here

Let’s connect on Twitter and LinkedIn

--

--