Part 4. Advanced Android Development - Dagger2
Dagger 2
Dagger 2 is a framework that has been developed to simplify the process of applying dependency injection. Once you have applied Dagger 2 in your project, you will find some classes automatically generated for Dependency Injection in your app. Look at one such snippet of generated code.
public final class ContextModule_ProvideUPayServiceFactory implements Factory {
private final ContextModule module;
private final Provider contextProvider;
private final Provider pStoreProvider;
private final Provider dStoreProvider;
public ContextModule_ProvideUPayServiceFactory(
ContextModule module,
Provider contextProvider,
Provider pStoreProvider,
Provider dStoreProvider) {
assert module != null;
this.module = module;
assert contextProvider != null;
this.contextProvider = contextProvider;
assert pStoreProvider != null;
this.pStoreProvider = pStoreProvider;
assert dStoreProvider != null;
this.dStoreProvider = dStoreProvider;
}
@Override
public UPayService get() {
return Preconditions.checkNotNull(
module.provideUPayService(
contextProvider.get(), pStoreProvider.get(), dStoreProvider.get()),
"Cannot return null from a non-@Nullable @Provides method");
}
As you can see this generated code is very well written and readable. Unlike Dagger 1 (the previous version of Dagger), Dagger 2 generates readable and clean code.
A caution before you start reading this paragraph. You can very well skip it 😛. Dagger 2 was released in 2016 which offered some vast improvements over the previous version. Most importantly, it solved the performance issue by replacing runtime reflection with compile-time code generation. It also solved some issues which Dagger 1 had with ProGuard.
From now on I would refer to Dagger 2 as Dagger and I might use DI for Dependency Injection. So I hope I am able to convince (coerce 😝) you to the fact that DI is great. I will explain how you can use DI to inject some SharedPreferences dependency wherever you want in your app. Using shared preferences in your app is very easy. So, this example may seem very trivial. But in this tutorial, I am targetting those developers who are a complete beginner at DI and I do not intend to overload them with advanced DI concepts. I may cover advanced DI concepts later. So let's dive in!
Step 1: Add dependencies to Gradle
Open up the app's build.gradle and add following dependencies for Dagger:
implementation "com.google.dagger:dagger:$rootProject.ext.daggerVersion"
implementation "com.google.dagger:dagger-android-support:$rootProject.ext.daggerVersion"
annotationProcessor "com.google.dagger:dagger-android-processor:$rootProject.ext.daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$rootProject.ext.daggerVersion"
If you do not understand where "daggerVersion" is defined, take a look at the second tutorial of the series.Step 2: Declare SharedPreferences in App
Step 3? Nope! Some Theory again 😈
As I told you earlier, Dagger uses annotations to generate code, it is imperative that we understand them first. The first such annotation is @Module. The @Module tells Dagger that the class which is annotated with it will provide dependencies for a part of the application. Typically, you will have multiple modules in a project with one of them providing app-wide dependencies. In this tutorial and the sample application, I have only discussed app-wide dependencies. I intended to keep this tutorial simple for you to follow. Again, I don't want you to lose your hair while following this series. I will include advanced examples of DI both in the sample application and this tutorial at a later stage. Take a look at the ContextModule class of the sample application as an example.
Now let's understand the second annotation @Provides. This annotation tells Dagger that this method provides a certain type of dependency, the one which is being returned. Again, take a look at the ContextModule class of the sample application and providePreferencesStore method inside it. This method tells dagger that I am providing you a dependency of UserPreferenceStore type.
There is another annotation which I would like to introduce. @Singleton annotation tells Dagger that there should only be a singleton instance of this dependency. As you can see the providePreferencesStore is annotated with it so that we have only one instance of SharedPreferenceStore in our app.
Step 3? Yes! Create Component
Well, you have already seen it! Implement a ContextModule in your application to provide the SharedPreferenceStore dependency. Now you have a module with dependencies which can be injected. But how can you use it? There is another annotation @Component which is needed now. Take a look at AppComponent class which has this annotation. @Component takes a list of modules. In the AppComponent we have added ContextModule and PresenterModule. You can ignore PresenterModule for now. This Component is a class that performs the actual injection into clients (like Activities, Fragments etc.). This class is not created by us. Dagger creates it for us through annotations. Take a look at this generated class:
public final class DaggerAppComponent implements AppComponent {
private Provider providePreferencesStoreProvider;
private MembersInjector weatherActivityMembersInjector;
private Provider provideWeatherPresenterProvider;
private MembersInjector weatherFragmentMembersInjector;
private Provider provideNotesListPresenterProvider;
private MembersInjector notesFragmentMembersInjector;
private Provider provideWeatherDetailPresenterProvider;
private MembersInjector weatherDetailFragmentMembersInjector;
private Provider provideAddNotePresenterProvider;
private MembersInjector addNoteFragmentMembersInjector;
private Provider provideNoteDetailPresenterProvider;
private MembersInjector noteDetailFragmentMembersInjector;
private Provider provideContextProvider;
private Provider provideOpenWeatherServiceProvider;
private MembersInjector weatherPresenterImplMembersInjector;
.................................................................................
Step 4: Make this Component Accessible
Create an interface like VividGraph with inject methods for all the clients. Here, the term Client means all the classes where you want to inject any dependency object. Make your AppComponent extend this interface. In the code above, you can see MembersInjector being created by Dagger for all of these clients!
Now create an AppComponentFactory class and you can copy the code. If you create your own Components with a different name, you only have to change those names and the code will work! Again, it is the magic of Dagger. Here, I have a created a PresenterModule as well. In your case you can delete that line. But you must have got the idea 😉. You can include as many modules as you want here with a simple line of code. You will notice an error on DaggerAppComponent. This happens because Dagger has not generated this class yet. This error would go away once you hit "Rebuild Project" or "Make Module app" from the build menu and let the process complete.
Now, the last step! In your Application class create an instance of the AppComponentFactory class inside onCreate. Just a few lines of code like here and you are done.
Step 5: Inject! Inject!
Take a look at the WeatherActivity class in the sample app. Inside onCreate of the class we call the inject method :
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); VividApplication.getComponent(this).inject(this); ........................... }
@Inject UserPreferenceStore _mPreferenceStore;
Dependency Injection is a vast topic. There are a few advanced concepts which I have skipped for now. I would come back to DI at a later stage to discuss them.
No comments: