Dependency injecton is when objects define their dependencies. Dependency Injection is build upon the concept Inversion of Control it is used to increase modularity of the program, make it extensible and make it easy to test.
Dagger is a dependency injection framework for both Java and Android. This time I will show you how to create a basic project using Dagger2.
Setup
Create a new project in android studio with an empty Activity, default options and set the following dependencies in build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "26.0.0"
defaultConfig {
applicationId "com.jos.dem.dagger"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
compile "com.google.dagger:dagger:2.11"
annotationProcessor "com.google.dagger:dagger-compiler:2.11"
provided 'javax.annotation:jsr250-api:1.0'
compile 'javax.inject:javax.inject:1'
}
Here we are using the annotationProcessor
for generating the dependency graph classes during build time.
Let’s create a basic model user class
package com.jos.dem.dagger.model;
public class User {
private String username;
private String email;
public User(String username, String email) {
this.username = username;
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Now let’s create few custom annotations: ActivityContext, ApplicationContext and PerActivity.
Activity Context
package com.jos.dem.dagger.context;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityContext {}
Application Context
package com.jos.dem.dagger.context;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {}
Per Activity
package com.jos.dem.dagger.context;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {}
@Qualifier
is used to distinguish between objects of the same type but with different instances. Now let’s create a UserService
as dependency to inject
package com.jos.dem.dagger.service;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.jos.dem.dagger.model.User;
@Singleton
public class UserService {
User user = new User("josdem", "joseluis.delacruz@gmail.com");
@Inject
public UserService(){}
public User getUser() {
return user;
}
}
@Singleton
Ensure a single instance exist globally. @Inject
on the constructor instructs the Dagger object when the class is being constructed.
Next, create DemoApplication
class that extends android.app.Application
package com.jos.dem.dagger;
import android.app.Application;
import android.content.Context;
import com.jos.dem.dagger.component.ApplicationComponent;
import com.jos.dem.dagger.component.DaggerApplicationComponent;
import com.jos.dem.dagger.module.ApplicationModule;
public class DemoApplication extends Application {
protected ApplicationComponent applicationComponent;
public static DemoApplication get(Context context) {
return (DemoApplication) context.getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
}
public ApplicationComponent getComponent(){
return applicationComponent;
}
}
You should add this class as application name in the AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jos.dem.dagger">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Dagger"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name=".DemoApplication"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Now let’s create class MainActivity
package com.jos.dem.dagger;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.jos.dem.dagger.component.ActivityComponent;
import com.jos.dem.dagger.component.DaggerActivityComponent;
import com.jos.dem.dagger.module.ActivityModule;
import com.jos.dem.dagger.service.UserService;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
@Inject
UserService userService;
private ActivityComponent activityComponent;
public ActivityComponent getActivityComponent() {
if (activityComponent == null) {
activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(DemoApplication.get(this).getComponent())
.build();
}
return activityComponent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivityComponent().inject(this);
setContentView(R.layout.activity_main);
TextView usernameTextView = (TextView) findViewById(R.id.usernameLabel);
TextView emailTextView = (TextView) findViewById(R.id.emailLabel);
usernameTextView.setText("Username: " + userService.getUser().getUsername());
emailTextView.setText("Email: " + userService.getUser().getEmail());
}
}
With the MainActivity
there is a activity_main.xml
file to represent graphical elements
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/usernameLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/emailLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Now let’s create ApplicationModule
This class defines the methods that provide the dependency. A Module class is identified by @Module
and the dependency provider method in identified by @Provides
.
ckage com.jos.dem.dagger.module;
import android.app.Application;
import android.content.Context;
import com.jos.dem.dagger.context.ApplicationContext;
import dagger.Module;
import dagger.Provides;
@Module
public class ApplicationModule {
private final Application application;
public ApplicationModule(Application application) {
this.application = application;
}
@Provides
@ApplicationContext
Context provideContext() {
return application;
}
@Provides
Application provideApplication() { return application; }
}
Now let’s create a component which links the DemoApplication
with the ApplicationModule
package com.jos.dem.dagger.component;
import android.app.Application;
import android.content.Context;
import com.jos.dem.dagger.context.ApplicationContext;
import com.jos.dem.dagger.DemoApplication;
import com.jos.dem.dagger.module.ApplicationModule;
import com.jos.dem.dagger.service.UserService;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(DemoApplication demoApplication);
@ApplicationContext
Context getContext();
Application getApplication();
UserService getUserService();
}
That’s it, we are injecting UserService
into our MainActivity
and with that we are getting user data information.
To download the code:
git clone https://github.com/josdem/android-dagger2-workshop.git