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 5.
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.
In part 4 we discussed:
- Refactoring Application class dependencies from App code to AppComponent.
- Refactoring of Bad ( Wrong ) practices for Glide and GitHubRepository.
- Dependency on Components ( HomeActivityComponent dependent upon AppComponent )
- Reusability of AppComponent into child components by adding as dependencies into Component.
Dagger 2 ( SubComponent ):
Today I am going to discuss with you SubComponent. Which is a new concept in dagger 2 series + that will remove one ambiguity which I faced for a long time.
(Note: Those readers who are not following my previous posts and only want to read about SubComponent. Thay can skip next paragraph and they can start reading from Theory heading.)
First, what is the ambiguity? Before that, there is a possibility currently you guys are not facing this issue but later I am 100% sure you will ask this question to your self or maybe someone else will ask you this question. Which I am going to explain but before that we need to do revise somethings from our previous blogs.
As we remember in our HomeActivityComponent we removed all methods and we added a new one void inject(HomeActivity homeActivity).
After refactoring an above class as shown below:
To make things more clear, review our AppComponent as shown below:
So in above image. AppComponent has two methods. Then, I am initializing that AppComponent into App class. Then, I am using this AppComponent into our HomeActivityComponent as a dependency.
I hope everything is revised, now it’s time to share with you the question which I asked.
WHY AM I USING method declaration INTO OUR APPCOMPONENT?
WHY AM I NOT USING void inject(App app); INTO OUR APPCOMPONENT?
Like as shown below.
Every time when I try to do something like this. I always got a below error.
So every time when I try to do something like inject() in AppComponent. I faced the same issue and I always revert my code and start work without giving more time but this thing is always a curiosity for me. I try to search on Google, but not able to get proper words for searching, meanwhile, I am reading Dagger 2 generated code + trying to learn about the SubComponents. After spending a lot of time I figure it out why that is not possible.
So from here, my post will follow structure as mention below:
1. Theory:
There is one rule which you need to follow in Dagger 2.
- Parent Component always declares methods for providing dependencies to its dependent Components. Inject method/strategy will not work for Parent Components.
For example, in our app, we are using AppComponent as a Parent component because we used that as a dependency into out HomeActivityComponent and DetailActivityComponent.
I am going to take one more example so this confusion will remove forever. 🙂
Now, in this case, I want my next ChildChildComponent will take ChildComponent as a Parent Component. So as we already know we need to declare methods which I want to inject into my ChildChildComponent class. Otherwise, the code is not able to figure it out about the dependency and we will get an error like ‘Something cannot be provided…’.
So in above image, you can see. How things will work. One thing, I never faced this type of scenario in my architecture but I am 100% sure above example will work.
2. SubComponents:
This section is useful for two readers:
- Those who are trying to remove AppComponent method declaration boilerplate code.
- Those who want to learn about SubComponents.
From here I am going to create a new branch Part5SubComponent. Which I checkout from Part1, where we completed our application without Dagger 2. ( Strongly recommended to review Part1 branch code)
Now READERS, FRIENDS or GUYS try to forget dependencies Component concept or Parent Component concept in Dagger 2 meanwhile. You can say I am going to implement this same app with the new strategy, which is called SubComponents. You are not able to see any code here which is using Parent Component like in our all previous posts. We saw AppComponent as a Parent Component.
Also this time I am going to start top to bottom. So my refactoring is going to start from App Class not from HomeActivity. Last time I started from HomeActivityComponent as we remember in part 2.
First my application class:
So I am going to create an AppComponent, which will be empty for the time being and it’s AppModule. This module will give me GitHubRepository and Glide dependency.
AppModule is same like we have in our all other previous posts.
Next, I am going to create a HomeActivityComponent and it’s module.
Now in above image, you can see currently everything is same what we already did in previous posts. Here I want to mention one thing for those who are following my code line by line. I comment my one line in DetailActivity as shown in above image. So your code can run. Now when I try to compile my code, I got an error.
As we already know, our AppComponent is empty and due to that, this error is occurring. To solve this issue we can use our old strategy to declare method into our AppComponent but now here we are going to use SubComponent strategy to solve this issue. Now open HomeAcitivityComponent and change @Component to @SubComponent.
After changing the @Component to @SubComponent. IDE start showing me error as you can see in above image. ‘dependencies’ is not a valid keyword for @SubComponent. So from here, one thing is clear as we are going with SubComponent we are not able to add a dependency from any Parent Component.
One more important point. Dagger 2 always generate a class for Component. As we know DaggerAppComponent, DaggerHomeActivityComponent but in case of SubComponents. There is no Dagger 2 generated class. For the testing purpose after changing @Component to @SubComponent try to compile your code. After that, you are not able to get DaggerHomeActivityComponent class.
Now on this point app start compiling without any error. If you try to run your app you will get a crash which is in HomeActivity for GitHubRepository Null Pointer Exception. Because currently Dagger 2 don’t know how to inject GitHubRepository into HomeActivity. Last time that is getting from Parent Component but this time we removed that dependency as we change our @Component to @SubComponent.
Now time come’s to update our AppComponent so we can inject GitHubRepository properly into our HomeActivity.
As shown in above image, I added my SubComponent ( I refactor HomeComponent name to HomeSubComponent, so that will be easy in reading for everyone. ) into our AppComponent. Now we are done with SubComponent:). All of my readers’ congratulations, now you know what is SubComponent. It’s time to show you how we will use this SubComponent into our app.
Also, don’t take tension currently post is not finished I will clear all the things related to SubComponents. 🙂
As you can see I added one method with plus name. That is a just name you can use any name like add or anything else. I chose this name because most people are using this name in their tutorials. So there is one rule for SubComponent. SubComponent only and only have a One ParentComponent in which we will declare our SubComponent just like you can see in above image. Second, the syntax of SubComponent:
Return type as a SubComponent type method name ( Module of the SubComponent);
HomeSubComponent plus( HomeActivityModule module);
Only for revision purpose, how we are using HomeActivityComponent in our Dagger 2 before SubComponent:
Now with SubComponent:
Here you can write code how you want. I showed you in two ways one is commented out and one which we are using. Now after this, app start working properly.
(Note: Don’t click on the item because DetailActivity will crash due to GitHubRepository. 🙂 To manage that error you need to write a SubComponent for DetailsActivty also ).
Before going to start a comparison between Component and SubComponent. I am going to revise SubComponent.
1. SubComponent always has a One Parent Component in which we will declare our SubComponent.
2. There is no dependencies keyword for @SubComponent. ( Because that is a SubComponent, not a Component. )
3. There are no more Dagger 2 generated classes. So for SubComponent, we need to use ParentComponent with plus(..) method to inject dependencies.
Now I know up to this point few readers are 100% comfortable with SubComponents but I have a feeling majority readers has some questions and ambiguities. Which I am going to explain in next section.
3. Component Vs SubComponent:
To make things simple and clear I am going to show you one comparison between Component and SubComponent code graph.
Component:
In above image:
1. AppComponent contains two declarations.
2. AppComponent initializes into App class.
3. HomeActivityComponent is dependent upon AppComponent.
4. In HomeActivity on initialization of DaggerHomeActivityComponent, I am giving AppComponent object as a composition.
SubComponent:
In above image:
1. AppComponent contains SubComponent or SubComponents.
2. AppComponent initializes into App class.
3. SubComponent doesn’t know about his ParentComponent. That only providing its own dependencies by including Module.
4. In HomeActivity I am injecting SubComponent by using its Parent Component.
Now try to compare both code images or their descriptions. That will make some sense for you. Now if there is again some confusion I am going to make one more diagram after that everything should be clear.
4. Pictorial Diagram:
Hopefully, things will be clear now.
In case of Components, we are adding dependencies, which we are giving later as a composed object. That is the reason for my question. WHY I AM NOT ABLE TO USE INJECT IN APPCOMPONENT?. As you can see, AppComponent is like a composed object inside a HomeAcirtivytComponent. So HomeActivityComponent doesn’t know about AppComponent only that can delegate the dependency request. Like we have a Glide and GitHubRepository, so for that, we need to declare these two methods into our AppComponent because without this HomeActivityComponent not able to access these dependencies.
In case of SubComponents. All SubComponents knows everything about, what is available in AppComponent because these SubComponents contain inside the AppComponent. Due to that, in this case, I am not writing Glide and GitHubRepository methods because every subcomponent from AppComponent automatically knows about all these dependencies which are AppComponent is providing.
5. Dagger 2 Generated code comparison:
Now I am going to show you Dagger 2 generated code in both cases. So everything crystal clear for everyone. One important point if someone feels below code is complex for those don’t take tension our SubComponent post is completed. This is optional which may give you more sense to grasp this SubComponent concept.
Above image is a DaggerHomeActivityComponent code. I am not going to explain everything but truly saying if you guys try to read that is really simple code. Only the issue is long names and lot of underscores, if you try to change their names in your mind you will see that code is really simple and easy. Instead lot of people are writing same code on daily basis. Now I am going to only show you AppComponent composition. As you can see there are two arrows which basically are showing two private classes for my AppComponent dependencies. One is for GitHubRepository and the second one is for Glide. Inside these classes, we have get() methods. In which we are asking to appComponent please give me respective dependency. In 2.13 Dagger they optimized a lot of code. If you compare that code with the new version you will be amazed but that is some other day post topic. Now next is SubComponent.
Above image is DaggerAppComponent in SubComponent strategy. Same here I am not going to explain all the code but this code is really simple then the Component-based code. Ok now here as you can see plus() is override in AppComponent. In which we are initializing our HomeSubComponent. As we remember in our HomeActivity we called this method to initialize our HomeSubComponent.
@Override protected void onCreate(Bundle savedInstanceState) { .... App.getApp().getAppComponent() .plus(new HomeActivityModule(this)) .inject(this); .... }
I hope after seeing above lines of code. You can make some sense of Dagger generated code and our calling code. Now on the right side, we have a very important code. We can see like DaggerAppComponent.this.glideProvider. Its mean SubComponent is a part of an AppComponent. So we can directly access AppComponent dependencies into SubComponent and due to this, we are not declaring any methods in AppComponent when we are using SubComponent strategy.
Dagger 2 ( Caution: PLEASE TRY AT HOME ) Part 6
Conclusion:
Now I am not feeling well. So as a conclusion, I only want to say Thanks and HAPPY NEW YEAR :).
First of all, I am very very thank you for this generous spirit to share your knowledge.
I think there is one another approach to not declare methods in AppComponent and also not using SubComponent.
we can declare `void inject(AppModule)` method in the AppComponent class and in the HomeActivityModule when we need a dependency from AppComponent (something like glide) we can add an @inject magic word in front of fields.
I mean :
//….
@Provide
HomeAdapter homeAdapter(@inject Glide glide) …
//…
Am I right?
Thank you for appreciation. I think you are talking about Constructor Injection. Yes, we can but for that, you need to follow construction injection implementation steps.
sorry, I was wrong in writing the code part. the @Inject word must be under @Provide.
//….
@Provide
@Inject
HomeAdapter homeAdapter(Glide glide) …
//…
Can we say this is the Method Injection? is glide provide automatically?
Maybe we need to inject the AppComponent in the constructor of HomeModule for this approach?
Sami, Can you try to download code from github and try to implement one by one of all parts. I hope your confusion will remove. @Provide has different responsibilities. Where you are using that is not a proper place for @Provide. Please try one-time practice and then we can start our discussion. I am always available here.