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

I recommend implementing a single SharedPrefrences class in your app. You can look at the SharedPreferenceStore class in the sample application and the UserPreferenceStore interface which it implements. This is a pattern which I follow in every app. You can very well copy the SharedPreferenceStore class and use it in your app as well. You can thank me now 😝. I am using this class to store the latitude and longitude after reading the location. This is required to fetch accurate weather info from the API and present it on the screen. In the WeatherActivity class of the sample application, I am reading the location of the device. If you want a refresher as to how to read the location of the device, you can have a look at the class here.


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 dependencyNow 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 DaggerAppComponentThis 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);
        ...........................
    }

And voila! Just inject all the dependencies you want at the top of the class 😉

        @Inject UserPreferenceStore _mPreferenceStore;

Whenever you want any dependency object you can follow the same process. You can see several such examples in the sample application.

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:

Powered by Blogger.