This codelab is intended to be the first step to implement Dagger2 dependency injection framework to your project.
As an example we created a simple project which is using the GitHub API to get users and their repositories. Clone it and let´s start!
The first step is to add the Dagger-2.0.1 dependency and the Dagger-Compiler-2.0.1 for the code generation to our build.gradle file:
compile 'com.google.dagger:dagger:2.0.1'
apt 'com.google.dagger:dagger-compiler:2.0.1'
As the Dagger2 compiler is based on annotations, we need to provide the javax.annotation dependency as well. Note: they will not be available on runtime!
provided 'org.glassfish:javax.annotation:10.0-b28'
Since the compiler requires to read And to make the compiler work, we need to add the apt plugin
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
...
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
Sync the project and, if everything works, go to the next Part!
Scopes in Dagger2 is the mechanism to keep single instances of classes as long as their scope exist.
In this section we will create the scope ApplicationScope
to create instances which will live as long as the Application object (singleton).
To create a Scope we need to define an Annotation. We will create it under di/scopes
directory.
We could use the Singleton
Annotation as well (since it's the same implementation), but for readability and the learning-factor
we create our own.
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
Now that we created our first scope, let´s use it in our components.
Modules and Components are the main elements in Dagger2.
Modules are classes to provide dependencies. To create a module we need to annotate a class with @Module
. We can create it on the di/modules
directory. In the module, we can Provide the dependencies we need. For that we will use the @Provides
annotation and a Scope in which this dependency is available (in our simple case this is ApplicationScope).
@ApplicationScope
@Provides
GitHubApi provideGitHubApi(){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(context.getString(R.string.endpoint))
.build();
return restAdapter.create(GitHubApi.class);
}
The Components are the injectors, the link between the @Module
and the @Inject
. To create a Component we need to annotate an interface with @Component
and list the @Modules
we want to use within the application. We can create it on di/components
@Component(...)
public interface ApplicationComponent {
Context getApplicationContext();
GitHubApi getGitHubApi();
}
And now we need to link them together. We will add the Applcation Module to the @Component
annotation.
@Component(modules = ApplicationModule.class)
Ok, we have Modules, Components and they are linked together but, how to use them? Let´s inject!
We are going to inject the just created dependencies to our Activities. For that, we will use the @Inject
annotation at the member, we want to inject.
@Inject
GitHubApi service;
Note that we removed the private
modifier. The fields must not be private
to be accessible within the generated code Dagger.
But that´s not all. We need to tell Dagger, that our Activity is using our dependency graph. To do so, we create an inject
method in our component, which will be used in the Activity.
void inject(MainActivity activity);
We need now to initialize the Application Component. We are going to initialize it on the Application class using a helper class.
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
// your component stuff
...
final class Initializer {
private Initializer(){}
public static ApplicationComponent init(ApplicationModule applicationModule) {
return DaggerApplicationComponent.builder()
.applicationModule(applicationModule)
.build();
}
}
Now, rebuild the Project (Build=>Rebuild Project). This triggers apt to read the annotations and the dagger compiler to generate code.
We can then use this helper to initialize the Application Component in our Application class onCreate()
.
ApplicationComponent.Initializer.init(new ApplicationModule(this)).inject(this);
Then, on each activity we can get the Application Component and inject the activity to the dependency graph.
((DaggerApp)getApplication()).getApplicationComponent().inject(this);
Now build, run and enjoy!
In the tests, we want to use a mocked api service. Therefore we will create a module mock the api and use it in our application component.
First, we are going to include the needed test dependencies.
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'com.android.support:support-annotations:23.1.0'
androidTestCompile 'com.android.support.test:runner:0.4'
androidTestCompile 'com.android.support.test:rules:0.4'
Now we are ready to start! To create the mock module, we extend the ApplicationModule
and override the method, which is returning our api.
public class TestApplicationModule extends ApplicationModule {
...
@Override
protected GitHubApi provideGitHubApi() {
// our mocked api
}
The next step is to create the MainActivityTest class. There we will create the Application Component with the mock module.
@Test
public void testResponse() throws Exception {
ApplicationComponent applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new TestApplicationModule(null))
.build();
...
}
Once we have the Component, we set it to the Application class.
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
((DaggerApp)instrumentation.getTargetContext().getApplicationContext()).setApplicationComponent(applicationComponent);
The last step is to launch the activity and add some sleep to see the results:
activityTestRule.launchActivity(new Intent());
Thread.sleep(10000);
You are ready to run and see the mock response! It was easy, or? :)