BMI Calculator – Dagger

BMI Calculator – Dagger 2

In this lecture of the BMI Calculator sample application we are going to refactor our app and we are going to start using Dagger 2, which is a popular dependency injection library for Android.

GitHub

The starter project for this lecture is available on GitHub. Just download it using the below link.

GitHub – branch recyclerView

After the download, extract the app from the .ZIP file and import it in Android Studio (File -> New -> Import Project…)

Dependency Injection

First of all, we have to talk about Dependency Injection. In software engineering, dependency injection is a technique, a design pattern whereby one object supplies the dependencies of another object.

We can create the dependent object outside of the class and provide those object to class from different ways. DI can help with, moving the creation and binding of the dependent objects outside of the class that depends on them.

In this chapter we are going to refactor the code to use Dagger 2.

Dagger 2

Dagger is a dependency-injection library created by developers at Square in 2012. Dagger 1 used the concepts of reflections to create instances of the classes and dependencies.

After that, they collaborated with developers at Google to build Dagger 2 by removing reflections, which are very powerful.

Dagger 2 is a very popular dependency injection framework on Android. It works with annotation processors which generates readable code for the developer as well.

Most used annotations:

      • @Module and @Provides: Both define classes and methods which provide dependencies.

      • @Inject: It requests dependencies and can be used on a constructor, a field, or a method.

      • @Component: It enables selected modules and it is used for performing dependency injection.

Dagger 2 is based on Java, but it is fully operable with Kotlin also.

Step 1 - Add the dependency

We are going to start this chapter by adding the dependencies of Dagger to the app. For this open the Module build.gradle from the left Project pane.

Then add the below lines to the dependencies{} section of the file.


Dependencies of Dagger

Next sync the project by clicking on the Sync now button at the top right corner of Android Studio.

Step 2 - Build a modul for fragment

First of all, create a new package called “di” in the main source set. There we are going to create the files for Dagger.

Thenafter we are going to create a Modul for the fragments. The modul makes a normal class into a Dagger Module. The Dagger @Modules create the dependencies for the Dagger @Component.

Inside of the fragment builder module we gonna tell dagger that we need to use dependencies in this fragments, because for fragments we can’t make constructor.

So, create a new file called “FragmentBuildersModule” in the di package and paste into it the below lines.


FragmentBuildersModule

Dagger 2.11 introduce ContributesAndroidInjector, which frees us from creating a bunch of boilerplate code in order to create a Fragment Subcomponent or an Activity Subcomponent with their respective binding…, all you have to do is to use the @ContributesAndroidInjector annotation.

Source: Dagger 2 — Dependency Injection basics in Android

Now, open up all of the 3 fragments:

      • BmisFragment()
      • AddNewBmiFragment()
      • ResultFragment()

… and add the below onCreate() method to the classes.


AndroidSupportInjection

Using the AndroidSupportInjection.inject() method we bind the fragment to the dependency graph. It means, dagger will create for the fragments the injector factory.

Because we gonna create the module for the fragments inside of the MainActivity’s module, we have to modify a bit the MainActivity::class as well. So, open it from the main source set.

The class has to be inhereted from the DaggerAppCompatActivity::class. So, replace with it the current AppCompatActivity::class.

Then, add the below line to the onCreate() method.

AndroidInjection.inject(this)

Step 3 - Build a modul for fragments

Next, we are going to create the same module for the MainActivity as well.

So, create a new Kotlin file in the di package and name it as “ActivityBuilderModule”. Thenafter paste into it the below lines.


ActivityBuilderModule

Note thet in this case we have extended the @ContributesAndroidInjector annotation by FragmentBuildersModule::class what will bind the fragments mentioned in this moduel to the MainActivity::class.

Step 4 - Modul for the Room database

In this step we are going to create a modul for the Room database also. Using this modul we don’t have to initiate it all the time in the ViewModel classes, because we gonna inject the repository into the ViewModels. About this we are going to talk a bit later.

So again, create a new file in the di package with the name of “RoomDatabaseModule”.

Paste there the below code.


RoomDatabaseModule

Note thet we use here the @Singleton annotation. With @Singleton scope, Dagger 2 will make sure that any dependency using this scope will be created only once within the same component.

@Provides is used to register dependencies for injection. We are creating the instances of the required classes as return values of the methods, which are annotated with @Provides. It means it provides objects for dependency injection what we don’t own. In our case it is the Room database and DAO.

Step 5 - Create ViewModelFactory

In our app we are going to have one more module. This module will be responsible to bind the ViewModels. But we will refactor the ViewModels and we gonna inject the repository in their constructor. In order to be able to inject the ViewModel object, we need to use a factory class.

So, still in the di package, create a new file, called “ViewModelFactory”.

As constructor injeciton we will get first in this factory method an instance about the repository. Then the ViewModelFactory will provide it to the ViewModels.

Here it is:


ViewModelFactory

Note thet we use here the @Singleton annotation. With @Singleton scope, Dagger 2 will make sure that any dependency using this scope will be created only once within the same component.

@Provides is used to register dependencies for injection. We are creating the instances of the required classes as return values of the methods, which are annotated with @Provides. It means it provides objects for dependency injection what we don’t own. In our case it is the Room database and the DAO.

Next, we have to refactor the Fragment to start using the injection of the ViewModelFactory and let Dagger initiate the ViewModels for the Fragments.

So, first open the BmiFragment::class from the bmi package.

Currently we initiate the BmisViewModel using the AndroidX library. Replace it with the below lines.


Inject the ViewModelFactory

This is called field injection. Using the @Inject annotation we can inject the ViewModelFactory into the Fragment. We have to do it, because, we can’t use constructor injection in case of Activities and Fragments.

Then add the below line to the end of the onCreate() method.


Init the ViewModel

Do the same refactoring with the other Fragments as well!

Step 6 - Multibinding

To use the above created ViewModelFactory in Activities and Fragments for different ViewModels, we have to use Dagger’s multibindings feature, which allows us to create a “map” of objects with a specific key.

Dagger ( in compiling time) will create the map and we will provide them to ViewModelFactory::class as argument. So when we will invoke the create() method in the fragments, it will be able to pick the right instance from the map.

So, create a new Kotlin file in the di package with the name of “ViewModelKey”, and paste into it the below lines.


ViewModelKey

Step 7 - ViewModelModule

After the creation of the ViewModelKey, we can finally create the last module.

Again in the di package, create a new Kotlin file called “ViewModelModule”.


ViewModelModule

Step 8 - The Repository interface

Before we can start the refactoring the ViewModels, we have to refactor a bit the Room repository as well. So, open it from the data package.

First, move your cursor to the name of the class, and there press Shift+F6 (Win/Mac), raname it and all the usages to “BmiRepositoryImpl”.

Next, using your mouse, click with the right button on the name of the class. From the popup window select Refactor, then the Extract interface option.

Thenafter select the second “Extract to separate file” option.

In the popup window name the interface as “BmiRepository” and check in all the members. The rest can stay as it is, so click on the Refactor button.

Next step of the refactoring is to extend the header of the repository’s implementation to inject into it the BmiDao::class. So add the “@Inject constructor” to its header. You can see it below.


@Inject constructor

Step 9 - Refactoring of the ViewModels

Because we gonna create the Database and the DAO instances using Dagger and it will be singleton, which means only one instance will be created in the whole lifetime of the app, so we won’t need to do it inside of the ViewModels.

BmisViewModel::class

Open up the BmisViewModel::class from the bmis package.

Currently its header looks like below.


BmisViewModel::class

Change it to:


New header of the ViewModel

Note that we have changed the inheritance of the class from AndroidViewModel to ViewModel, because we don’t need anymore the context of the appliction in the ViewModel.

Thenafter you should have an error, because the variable of the repository is already defined in this class. To fix this issue, remove the variable of the repository from the body of the ViewModel. Then remove the init block also.

After the refactoring, the BmisViewModel::class should look like below.


The refactored ViewModel

AddNewBmiViewModel::class

The next step is to add the constructor injection to the AddNewBmiViewModel::class also. So, open the file from the addnewbmi package.

The task here is just to extend the header of the class with “@Inject constructor”.


Refactoring of the AddNewBmiViewModel

ResultViewModel::class

The last ViewModel is the ResultViewModel. Here our task is the same like it was in case of the BmisViewModel:

      1. Inject the repository in the constructor
      2. Remove the variable of the repository from the body of the class.
      3. Remove the init block.

Thenafter the ResultViewModel looks like below.


Refactoring of the ResultViewModel

Step 10 - The Application class

In the next step we are going to create the AppComponent. Since we want AppComponent to live for the whole duration of the app, we instantiate it in our Application::class.

In this case our dependencies with @Singleton scope will be the same during the lifetime of the app. Knowing when a Dagger Component gets destroyed depends on where we instantiated it.

To have the AppComponent available in the whole app, we have to create a class, which extends the Application::class.

So, create a new file called “MyApp” in the root of the main source set and extend the class by the Application() class and by HasAndroidInjector interface.

Using the HasAndroidInjector interface we can reduce some boilerplat code in our Application class. 

You can read more about the HasAndroidInjector interface under the link:
Reducing Boilerplate with the new HasAndroidInjector in Dagger 2.23

Thenafter we have to implement a method, so paste into the MyApp::class the below lines.


AndroidInjector

One more thing what we should do in this step is to add this MyApp to the AndroidManifest.xml file, what you can find in the manifest folder.

Add the below attribute into the tag, but before the .

android:name=“.MyApp”

Step 11 - The AppComponent

If you want to give Dagger the ability to inject dependencies in a class’ member variables that we specified, then we have to create a @Component.

A @Component makes a normal interface into a Dagger Component. It is an interface that exposes dependencies that can be requested and the classes that request these dependencies such as Activities/Fragments in Android.

Source: arthlimchiu.com

The last file, what we are going to create is the AppComponent. So create it as a Kotlin file in the di package.

First we gonna use the @Singleton annotation, since we want AppComponent to live for the whole duration of the app.

As we have talked about, we gonna have here the @Component annotation as well, where we initiate the moduls as well.

We had to add the provided AndroidSupportInjectionModule (or AndroidInjectionModule if you are not using Android support library) since we are using @ContributesAndroidInjector.

So, finally the AppComponent looks like below.


AppComponent

Our last step is to let Dagger generate its classes, create its dependencies. We can do thet if we rebuild the app. So, in top menu select the Build option and there the Rebuild Project.

It can take some minutes, so relax and drink a coffee. 😎

After a successful build open the MyApp::class and override its onCreate() method. In this method we gonna initiate the AppComponent. We can do it using a class, which was autogenerated by Dagger.

When you create a new @Component interface, then Dagger will generate for it a class, which starts with Dagger and the rest is the name of your component. In our case it looks like this:

DaggerAppComponent

The onCreate() method of the MyApp::class.


onCreate()

Run the app

Finally, it’s time to run the app. It should work as before. 😎

Congratulations!
You just started using Dagger 2 dependency injection in you app. 😊

GitHub

The source code is available on GitHub under the branch dagger2, check out using the below link.

GitHub – dagger2

Questions

I hope the description was understandable and clear. But, if you have still questions, then leave me comments below! 😉

Have a nice a day! 🙂

Follow and like us:

Click to rate this post!

[Total: 0 Average: 0]

Leave a Reply

Your email address will not be published. Required fields are marked *