WOW, we got one more day so it’s time to make this day awesome by learning something new.?
Hello friends, hope you are doing good. So today we are going to enjoy the next part of Dagger 2 and that is part 4.
Motivation:
Motivation is same which I already shared with you in part 1.
Revision:
In part 1 we discussed:
- What is a dependency?
- What is a dependent?
- What is a Dependency Injection ( DI )?
- Famous techniques/strategies for DI ( Constructor Injection, Method Injection, Field Injection )
- How to make a dependency graph from code.
In part 2 we discussed:
- Annotation Processing
- Magic words
- @Component, @Module, @Provide
- How to move dependencies into component
- A relationship between component and module
In part 3 we discussed:
- Refactor all dependencies from App code to Component
- Tips for “how to read the error on a console”
- @Inject Magic Keyword
- Abstraction Between AppCode and Creation Code.
Dagger 2:
Today we are going to refactor our remaining code. As we already know in last part we completed our refactoring of a HomeActivity class. Our all dependencies are shifted from App code to Module. In which some dependencies we are using in a wrong way like GithubRepository.
@Module public class HomeActivityModule { ... @Provides public HomeAdapter homeAdapter() { return new HomeAdapter(new ArrayList<>(), glide); } @Provides public GitHubRepository gitHubRepository() { return App.getApp().getGitHubRepository(); } ... }
Now here I have two choices to start my this post.
1. I will create a separate Component without taking care of my old posts material and used that component to our current code.
2. I will do refactoring from the point where currently we leave our last post.
After taking a long time, I took a decision I will do both. So those readers who are following me they will learn how to refactor their current projects by using Dagger 2. Second those readers who also keen to learn, how to start a new greenfield project by using Dagger 2 they also get the benefit of my writing. Because both require different app design thinking.
Refactoring where we stop our last post: (GitHubCode Part4WithRefactoring)
You can say I am going from bottom to top approach. Mostly when we are doing refactoring we always do bottom to top. I am showing you now what we did in last 3 posts below.
In above image, I have Class, Component, and Module. HomeActivityComponent is in final shape. You can say there is no more refactoring required for our component. Next, in HomeActivityModue we have one issue, which we need to refactor. As you can see underline in the left corner. App.getApp().getGitHubRepository();. We need to refactor this line then on the right side again we need to refactor one more line. App.getApp().getGlide(). if you guys remember, I told you in our last blog post I am using these objects in a wrong way. Now it’s time to learn how to use these objects in a right way. First, what is the issue in above code?
Basically, we are directly dependent on App class. Which is not good especially when we are using Dagger 2, instead we are using Dagger 2 to manage our dependencies. Here we need to create some strategy so we can get these dependencies from Dagger 2 and not by direct using of App class.
So friends how we can start our refactoring?
Yes, you guys are awesome. We will go first to the class where we are doing initialization of these objects.
public class App extends Application { private static App app; private GitHubRepository gitHubRepository; private Glide glide; @Override public void onCreate() { super.onCreate(); app = this; gitHubRepository = GitHubRepository.getInstance(GitHubServiceGenerator.gitHubService("https://api.github.com")); glide = Glide.get(this); } public static App getApp() { return app; } public GitHubRepository getGitHubRepository() { return gitHubRepository; } public Glide getGlide() { return glide; } }
We already know Application class is a Singleton class in Android. It’s mean all objects which initialized here are also singleton but that is a discussion when we will discuss Scopes in Dagger 2, currently, we are going to refactoring of our current code. So first step is done. We know we are initializing GitHubRepository, Glide in App class. The second step we need to create a Component because we know AppCode always ask to Component for dependency.
Now I need to create two methods on for GitHubRepository and second for Glide.
@Component public interface AppComponent { GitHubRepository getGitHubRepository(); Glide getGlide(); }
Third step we need to create a Module from where Component will ask for the object.
Revision: AppCode -> Component -> Module
In above code, I only moved my initialization code from App class to AppMopdule. Next, there is some issue with above code. First I will fix the Glide issue. Basically, glide is asking for App context. Which I can manage by sending through the constructor of AppModule.
@Module public class AppModule { private final Context appContext; public AppModule(Context appContext) { this.appContext = appContext; } @Provides public GitHubRepository gitHubRepository() { return GitHubRepository.getInstance(GitHubServiceGenerator .gitHubService("https://api.github.com")); } @Provides public Glide glide() { return Glide.get(appContext); } }
Second I am using direct hard code URL. Which may be ok for some developers but I am going to inject that by AppModule constructor.
@Module public class AppModule { private final Context appContext; private final String url; public AppModule(Context appContext, String url) { this.appContext = appContext; this.url = url; } @Provides public GitHubRepository gitHubRepository() { return GitHubRepository.getInstance(GitHubServiceGenerator .gitHubService(url)); } @Provides public Glide glide() { return Glide.get(appContext); } }
Currently, there is more refactoring required in GitHubRepository but that I will do later. It’s time to use our AppComponent in our app.
public class App extends Application { private static App app; // private GitHubRepository gitHubRepository; // private Glide glide; private AppComponent appComponent; @Override public void onCreate() { super.onCreate(); app = this; appComponent = DaggerAppComponent.builder() .appModule(new AppModule(this, "https://api.github.com")) .build(); // gitHubRepository = GitHubRepository.getInstance(GitHubServiceGenerator.gitHubService("https://api.github.com")); // glide = Glide.get(this); } public static App getApp() { return app; } public AppComponent getAppComponent() { return appComponent; } // public GitHubRepository getGitHubRepository() { // return gitHubRepository; // } // // public Glide getGlide() { // return glide; // } }
After adding AppComponent in our AppClass our code looks like as shown above. Now I am going to remove comment lines. So we can see clear picture easily :).
public class App extends Application { private static App app; private AppComponent appComponent; @Override public void onCreate() { super.onCreate(); app = this; appComponent = DaggerAppComponent.builder() .appModule(new AppModule(this, "https://api.github.com")) .build(); } public static App getApp() { return app; } public AppComponent getAppComponent() { return appComponent; } }
BOOM. Now if you try to run the code, that will not work. (Note: If you want to check what type of bugs or errors you will get up to this point, you can check from this commit. )
Here we are getting 4 errors after refactoring of App class. Currently here if you saw we are getting two errors in DetailActivity class. Which currently we are not using for our tutorial. So, for the time being, I am going to use a bad approach in Detail Activity class to resolve these bugs. So we can run our app and for other 2 errors, we already know we are doing refactoring. Fix for DetailActivity as shown below.
After temporarily fix Detail Activity class. Now I have 2 more errors.
Before going to fix these bugs. I want to revise one concept of Dagger 2.
Component -> Maybe Dependent on -> Component or Components
So it’s time to use above concept in our current scenario. As we know Glide and GitHubRepository we can get from AppComponent. It’s mean I can add AppComponent as a dependency on our HomeComponent class.
@Component(modules = HomeActivityModule.class, dependencies = AppComponent.class)
public interface HomeActivityComponent {
void inject(HomeActivity homeActivity);
}
As we can see in above code. How we added a dependency on our component. One small point if we want to add more than one dependencies then we can use curly braces as shown below.
dependencies = {AppComponent.class, AComponent.class, BComponent.class, }
Next, I am going to remove Glide construction injection from our HomeActivitytModule.
Before Refactoring:
@Module
public class HomeActivityModule {
private final Context context;
private final Glide glide;
public HomeActivityModule(Context context, Glide glide) {
this.context = context;
this.glide = glide;
}
...
@Provides
public HomeAdapter homeAdapter() {
return new HomeAdapter(new ArrayList<>(), glide);
}
...
}
After Refactoring:
@Module
public class HomeActivityModule {
private final Context context;
public HomeActivityModule(Context context) {
this.context = context;
}
...
@Provides
public HomeAdapter homeAdapter() {
return new HomeAdapter(new ArrayList<>(), glide); // Currently glide is giving error
}
...
}
Now I am going to fix our HomeActivity code. I need to remove Glide object from HomeModule in HomeActivity.
public class HomeActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); // Before Refactoring // DaggerHomeActivityComponent.builder() // .homeActivityModule(new HomeActivityModule(this, App.getApp().getGlide())) // .build().inject(this); // After Refactoring DaggerHomeActivityComponent.builder() .homeActivityModule(new HomeActivityModule(this)) .build().inject(this); ... } ... }
Great. Now we have only errors in our HomeActivityModule.
Here I am going to do some magic. Maybe at first glance that will give you tough time but do not take tension, I will explain that magic. As we already know in computer science there is no magic everything has a code.
Fixed version of above class code as shown below.
Now in homeAdapter, I am providing Glide as a param and I removed GitHubRepository provider. After that, if you compile our code that will work but if you run you will get a runtime exception which is shown below. ( If you are confused about, from where glide is coming and how gitHubRepo work by removing. Only wait I will explain 🙂 ).
Basically, this exception is saying. Our HomeActivityComponent is dependent on AppComponent but when we are initializing that Component in HomeActivity class, we are not providing that dependency. So please provide that dependency so Dagger 2 can work for you.
public class HomeActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); // Before Refactoring // DaggerHomeActivityComponent.builder() // .homeActivityModule(new HomeActivityModule(this)) // .build().inject(this); // After Refactoring DaggerHomeActivityComponent.builder() .appComponent(App.getApp().getAppComponent()) .homeActivityModule(new HomeActivityModule(this)) .build().inject(this); ... } ... }
After providing AppComponent, my app starts working without any issue. Wow.
Now it’s time to expose the magic of Glide as param and how GitHubRepository working in HomeActivity without providing in HomeActivityModule.
In above image, If you review HomeActivityComponent. Currently, we are not adding an AppComponent module. So if we run this code that will show an error because you can see GitHubRepository is not available and same for Glide. Now I am going to add AppComponent.
Here we can see there is no magic. Behind the scene when I added AppComponent, Dagger 2 now know how to get GitHubRepository and Glide. So as in HomeActivityModule I am asking for Glide object as param. Dagger 2 will provide from AppComponent. Basically, there is a boilerplate code which is generated by Dagger 2 on our half. In the same way, when in HomeActivity Dagger 2 reading @Inject GitHubRepository, he will figure it out he has already that object due to added AppComponent dependency. That is really simple.
Now I want to say sorry. Today I decided to complete bottom to top and top to bottom ( Top to bottom -> when we are going to start a new project. How to finalize architecture with Dagger 2) but I have a feeling I already have a very important concept in this blog. Which may give tough time to my readers at the start. That’s why I will start remaining concepts in other blog posts.
Dagger 2 ( Caution: PLEASE TRY AT HOME ) Part 5
Conclusion:
Now you can see one more benefit of Dagger 2. Anywhere I want to use Glide or GitHubRepository I will add AppComponent as a dependency. Second, that is an Application level so I always got the same object, no new object creation. Third, things are really simple. Like I need a Glide object in my module HomeAdpater object creation. I only added a one param with Glide without writing any extra code, like how that will be given to this method but that is working. Now as you guys know behind the scene all code is written by Dagger 2 but as a developer things are very easy. Instead, I am writing less code and as we know less code means fewer bugs. 🙂
Don’t take care of the below image. That is basically generated by Dagger 2. Only for those who are really keen to know. How Dagger 2 behind the scene generating code for our HomeActivityComponent.
OK, Guys. We will meet in next post. BYE.
Finally, after reading all 4 parts, I know what dagger is ! Thanks for the post !
Agree!
Thanks so much!
Thank you very much for appreciation. 🙂
Due to your comment, I can say what I want I achieved from mine blogs.
Thank you very much for appreciation. 🙂
Due to your comment, I can say what I want I achieved from mine blogs.
awesome works ! cant believe I just learnt Dagger2 😛
Thank you for comment. 🙂