Dependency Injection and Dagger on Android
By: Kayvan Kaseb
Tags: Dependency Injection, Android, Dagger 2, Android Development, Android App Development

Dependency Injection and Dagger on Android

As we know, there are a number of objects would be instantiated in developing an Android app, and these objects would require other dependencies as well. Furthermore, Dependency Injection(DI) plays an essential role for having a high quality software particularly an Android app. As a result, Dagger 2 has been built for Android developers as one of the most effective and popular dependency injection frameworks for tackling dependencies in Android apps. This essay aims to discuss the Dependency Injection and Dagger for Android developers.

Overview

In fact, Dependency Injection is a technique that one object provides the dependencies of another object. A “dependency” is an object that could be used as a service. The “injection” refers to the passing of a dependency (a service) into the object (a client) that would use it. Dependency Injection in build upon the concept of Inversion of Control, which indicates a class should get its dependencies from outside. In simple words, no class should instantiate another class; however, should get the instances from a configuration class. It is the fifth principle of SOLID, which states a class should depend on abstraction and not upon concretions (hard-coded). In short, if a class creates an instance of another class via the new operator, it cannot be used and tested independently from that class that is called a hard dependency. For instance, A Car depends on wheels, engine, fuel, battery, and so on. So, if we want to create a Car class in general, we have the following class:

class Car{ Engine engine;

Wheels wheels, Car(){

engine = new Engine();

wheels = new Wheels();

} void drive(){ //The rest..

}

}

However, the Car object should not be responsible for creating and knowing its parts in detail. Instead, we should pass them from outside the class. After using Dependency Injection, the Car class should be changed as below:

class Car{ Engine engine;

Wheels wheels, Car(Engine engine, Wheels wheels){

this.engine = engine;

this.wheels = wheels;

} void drive(){ //The rest..

}

}

Thus, we decoupled the construction process of the Car from the construction process of Engine and Wheels. It means we can easily modify these classes outside of the Car class without having hard-coded.

Fundamentally, there are three types of dependency injection:

constructor injection: the dependencies are provided through a class constructor. setter injection: the client exposes a setter method, which the injector uses to inject the dependency. interface injection: the dependency provides an injector method that will inject the dependency into any client(object) passed to it. Clients must create an interface, which exposes a setter method for accepting the dependency.

All in All, a dependency consumer asks for the dependency or object from a dependency provider through a connector.

Eventually, we should follow some steps for implementing Dependency Injection in our projects: creating the objects, knowing which classes require those objects, and providing them all these objects.

Advantages of using Dependency Injection

Ease of testing such as using mock objects.

2. It decreases coupling between a class and its dependency.

3. It can enhance the re-usability of codes.

4. Ease of refactoring by focusing on smaller classes.

5. It reduces boilerplate code by handling dependencies with components.

6. Ease of extensibility of software.

Additionally, some signs for identifying Software Rot would be eliminated by using Inversion of Control in general:

Rigidity: small changes leads to rebuild the entire software. Fragility: changes to one component causes other unrelated components to misbehave. Immobility: if a component cannot be used in another system, this component is considered as immobile. Immobility usually is caused by couplings and dependencies among various components.

What is Dagger 2?

Initially, Dagger library was created by developers at Square. Dagger 1 was used to create instances of classes and inject dependencies via Reflections. After improving upon the Dagger 1, and collaborating with a team of developers at Google, Dagger 2 has been built much more faster. Also, the improved version was introduced without using Reflections. So, Dagger2 is a well-known dependency injection framework for Android developers. It provides a solution for tight coupling problem in apps. In addition, Dagger2 uses code generation based on annotations and compile-time checks to analyze and verify dependencies.

The main annotations that are used in Dagger 2 can be categorized in three section as follows:

Provider

Classes annotated with @Module are responsible for providing objects, which can be injected. In other words, Module classes and their methods provide dependencies. Dagger will know where to find the dependencies in order to support them when you are constructing class instances. Module classes define methods annotated with @Provides . This means the returned objects from these methods are available for dependency injection.

2. Consumer

The @Inject annotation is used to request dependencies, and could be used on a constructor, a field, or a method as well.

3. Connector

A @Component interface defines the connection between provider of objects (modules) and the objects which indicates a dependency. In short, the Module class does not provide the dependency directly to the class that is requesting it. For accomplishing this case, a Component interface is used as a bridge between Inject and Module.

In addition to these sections, you can be able to use the @Singleton annotation to indicate that there should be only one instance of the object.

An example for implementing Dagger in Android apps

After setting up and adding some required dependencies for working with Dagger in Android Studio, the most important steps are mentioned for using Dagger in android project as follows:

Declaring the Module classes

One of the main steps for implementing Dagger in Android apps is declaring Module classes. For instance, if want to save and retrieve some data in our project by using SharedPreferences, we can create a class that is annotated with Module as below:

@Module

public class SharedPreferenceModule { private Context context; public SharedPreferenceModule(Context context) {

this.context = context;

} @Singleton

@Provides

public Context provideContext() {

return context;

} @Singleton

@Provides

public SharedPreferences provideSharedPrefs(Context context) {

return PreferenceManager.getDefaultSharedPreferences(context);

}

}

Besides, if we want to work with SharedPreferences, we need an Application context. Another Dagger module, AppModule.java , will be used to provide this reference. We can define a method annotated with @Provides that informs Dagger that this method is in charge of providing the instance of the Application class:

@Module

public class AppModule {



Application mApplication;



public AppModule(Application application) {

mApplication = application;

}



@Provides

@Singleton

Application providesApplication() {

return mApplication;

}

}

Indicating Injection target

Next, you should assign references in our activities, services, or fragments to have access to providers in a class. We must need to annotate this class with a @Component annotation. Additionally, the activities, services, or fragments that are allowed to request the dependencies declared by the modules via @Inject annotation should be declared in this class with individual inject() methods:

@Singleton

@Component(modules={AppModule.class, SharedPreferenceModule})

public interface AppComponent {

void inject(MainActivity activity); // Other Activities and Fragments that are allowed to request dependencies should mention here!



}

Also, the MainActivity should be like this:

public class MainActivity extends Activity { @Inject

SharedPreferences sharedPreferences; //it will lead to find a provider that returns a SharedPreferences!



public void onCreate(Bundle savedInstance) { // set singleton instances to fields

AppComponent.inject(this);

}

Instantiating the component

Now, we should specify our component and list of Modules that are part of it in a class, which extends the Application class because these instances should be mentioned just only once throughout the entire lifecycle of the app:

public class SampleApp extends Application {



private AppComponent appComponent;



@Override

public void onCreate() {

super.onCreate();





AppComponent = DaggerAppComponent.builder()



.appModule(new AppModule(this))

.sharedPreferenceModule (new SharedPreferenceModule(this))

.build();



}



public AppComponent getAppComponent() {

return appComponent;

}

}

As we know, we have extended from Application class; therefore, we have to declare this new class in AndroidManifest.xml as follows:

<application

android:allowBackup="true"

android:name=".SampleApp">

Finally, we should change the code that we mentioned before in onCreate() method in MainActivity class because we have to cast to SampleApp in order to get the right method:

((SampleApp) getApplication()).getAppComponent().inject(this);

Qualifier

If we need to use two different objects of the same return type, we can use the @Named qualifier annotation. Furthermore, Injection will require these named annotations too. So, it should be added this annotation to Inject annotation. Although @Named is a qualifier that is defined by dagger, you can create and customize your own qualifier annotations in Dagger 2.

Dependent Components and Sub-Components

Basically, if you want to have multiple components that do not need to remain in memory all the time such as components, which are tied to the lifecycle of an activity or fragment, we can implement dependent components or subcomponents. In either case, it provides an approach for encapsulating your codes.

Component dependencies: Use this when you want to keep two components independent.

Sub-components: Use this when you want to keep two components coupled.

Scopes

You can specify how components should be encapsulated by defining custom scopes. For example, you can create a scope that only lasts the duration of an activity or fragment lifecycle. You can clarify any number of custom scope annotations in your app by indicating them as a public @interface :

@Scope

@Documented

@Retention(value = RetentionPolicy.RUNTIME)

public @interface SampleActivityScope

{

//...

}

Benefits of using Dagger 2

Simple access to shared instances.

2. Easy configuration of complex dependencies in android projects.

3. It provides an adaptive environment that is useful for unit testing, integration testing, and other configuration changes.

4. You can be able to handle your scope of instances.

5. It provides modular approach, which leads to code easily maintainable.

In conclusion

In spite of the fact that Dagger 2 has been built for Android developers as one of the most efficient and well-known dependency injection frameworks for addressing dependencies in Android apps, Dagger has some limitations. For instance, Dagger does not inject fields automatically, and it cannot inject private fields as well.