Skip to content

Commit bafa323

Browse files
authored
Merge pull request #59 from moldedbits/dagger2-subcomponents
Dagger2 subcomponents
2 parents 0cc3c2d + a1ea7fe commit bafa323

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
layout: post
3+
title: "Using Dagger2 SubComponents to Propagate Dependancies"
4+
date: 2018-01-09 8:00:00
5+
author: abhishek
6+
categories: Android, Dependency Injection
7+
---
8+
dependency Injection makes your code scalable and testable. `Dagger2` is almost de-facto dependency Injection framework in Java these days, and, Its supported and used by Google which makes it first choice of all Android Developers.
9+
10+
In this post I am going to demonstrate use of `Subcomponents` in a very common android app use-case and show how dependency Injection is done from top to bottom. [In this app](https://github.com/abhishekBansal/android-mvp-retrofit2-dagger2-rxjava2-testing) user can see list of movies and click on any list item to see its details. Movie list is fetched from remote API, result of this API are cached in local database and this database is then used to present movie details on movie detail screen.
11+
<table>
12+
<tr>
13+
<td><img src="{{site.url}}/assets/images/movie-list.png" alt="Movie List" style="width: 200px;"/></td>
14+
<td><img src="{{site.url}}/assets/images/movie-detail.png" alt="MovieDetail" style="width: 200px;"/></td>
15+
</tr>
16+
</table>
17+
18+
#### Lets start!
19+
20+
### Identify Top Level Dependancies
21+
These are the `Singletons` that you usually have in your app like `Retrofit APIModule`, `SharedPreferences`, `DatabaseWrapper`, `ApplicationContext` etc. It makes sense to initialize them once and then use everywhere, only problem with that is, they make unit testing Hell!! So in modern TDD world we avoid `Singletons`. `Singletons` are bad for health of testable code. With `Dagger2` we still initialize them once and then use it everywhere but, these dependancies are now injected instead of doing `Someclass.getInstance()`. In our movie listing app I can see following global dependancies
22+
1. ApiModule- Provides access to retrofit for networking in our presenters.
23+
2. DatabaseInteractor- Database handler which can be used for inserting and retrieving data from local database.
24+
3. Application- Android application context
25+
26+
### Create Top Level Component and Modules
27+
Once we are done Identifying our `Singletons` we are ready to create a Application level `Component`. A `Component` in `Dagger2` is something which provides(or supplies) dependancies to injection target(i.e. the object which need dependancies). `Component`s are interfaces between `Module` and Injection Target. `Module`s are classes which do actual work of instantiating dependancies or creating actual objects which `Component` will supply to Injection Targets.
28+
29+
In movie example there are two top level modules which provide dependancies `AppComponent`. One is `ApiModule` which provides `Retrofit` instance for networking other is `AppModule` which provides other global objects like `Application` and `Database`.
30+
31+
Here is a diagram explaining flow that we are going to implement in this app
32+
33+
<img src="{{site.url}}/assets/images/dagger-schema.png" alt="Dagger2 Block Diagram" style="width: 800px;"/>
34+
35+
Let start diving into code. We will break and understand above diagram piece by piece. Here is code for `ApiModule`
36+
37+
```java
38+
@Module
39+
public class ApiModule {
40+
41+
@Provides
42+
@Singleton
43+
ApiService providesApiService() {
44+
OkHttpClient.Builder builder = new OkHttpClient.Builder();
45+
builder.hostnameVerifier((str, sslSession) -> true);
46+
47+
...
48+
...
49+
...
50+
51+
Retrofit retrofit = new Retrofit.Builder()
52+
.baseUrl(BuildConfig.API_URL)
53+
.client(builder.build())
54+
.addConverterFactory(GsonConverterFactory.create(gson))
55+
.addCallAdapterFactory(rxAdapter)
56+
.build();
57+
return retrofit.create(ApiService.class);
58+
}
59+
}
60+
```
61+
62+
Except for almost standard `Retrofit` initialisation code which is omitted for brevity sake, there are a few annotations which need special mention 🎉
63+
##### 1. @Module
64+
`@Module` tells `Dagger2` that following class is a `Module` and it will provide a bunch of dependancies. We already know what a `Module` is so Nothing fancy here!
65+
66+
##### 2. @Provides
67+
`@Provides` is an annotation which is for methods in a `Module`. It tells `Dagger2` that following method is responsible for constructing a dependency and it knows how to its job. `Dagger2` will just invoke this method whenever a dependency specified with method's return type is required. Note that method name here does not matter, it can be anything just the return type matters.
68+
69+
##### 3. @Singleton
70+
Last but most interesting annotation in above code is `@Singleton`. This annotation specifies scope of the dependency being provided. `Singleton` is most wide scope that can be used in `Dagger2`. A `Singleton` dependency is initialized once **during `Component`'s lifecycle** in `Dagger2`.
71+
72+
That's that, since now we have all the basics and our dependancies ready for injection lets go ahead and create our top level component called `AppComponent` in movie example.
73+
74+
Here is the code for simplified version of `AppComponent`
75+
```java
76+
@Singleton
77+
@Component(modules = {AppModule.class, ApiModule.class})
78+
public interface AppComponent {
79+
void inject(BaseApplication baseApplication);
80+
}
81+
```
82+
Lets have a look at new annotation introduced here
83+
84+
##### @Component
85+
`@Component` tells dagger that following interface is a `Component` and you need to generate implementation for it. As soon as you write `@Component` and hit build in Android Studio, `Dagger2` generates implementation for the target interface. `Dagger2` adds a prefix `Dagger` in name of implementation class. For an example for our `AppComponent` dagger will generate a class called `DaggerAppComponent`. Also as it apparent from code `@Component` also takes modules which will construct and provide dependancies.
86+
87+
`Dagger2` dictates that in order to provide scoped dependancies(in this case `@Singleton`) corresponding component need to have scope annotation. Thats why our app component is annotated with `@Singleton` annotation.
88+
89+
### Get Set Inject!
90+
91+
We have everything that we need, lets inject these dependencies so that we can actually use them. Since this is a global component, we will initialize it in `BaseApplication` class
92+
93+
```java
94+
public class BaseApplication extends Application {
95+
@Getter
96+
private AppComponent appComponent;
97+
98+
@Override
99+
public void onCreate() {
100+
super.onCreate();
101+
appComponent = DaggerAppComponent.builder()
102+
.appModule(new AppModule(this))
103+
.build();
104+
105+
appComponent.inject(this);
106+
}
107+
}
108+
```
109+
Most interesting piece of code is the one building dependency graph. Note that even though `AppComponent` uses two modules only one is specified here, that is because `ApiModule` has no dependencies. Dagger can figure that out by itself and creates it using default constructor.
110+
111+
### Where are my screens ?
112+
Now that we are done setting up global dependencies, about time we start creating screens. Our first screen is movie list screen. It follows standard MVP architecture where we have a `Fragment` inside and `Activity` as View, a `Presenter` and a `Contract` interface that defines language in which View talks to Presenter.
113+
114+
Our view will thus need and instance of `Presenter`. `Presenter` is responsible for fetching data from API and providing it to View, so it will need `ApiService` and `View`. **`Presenter` is where we will keep our business logic and it should not have any Android stuff so that we can unit test it easily.**
115+
116+
There are two ways in which `Dagger2` can pass dependencies to children
117+
1. **Dependent Component**
118+
A component can be dependent on other(or parent) component. In this case dependent component needs an instance of parent component to access dependencies which are satisfied by parent. This requires us to initialize all the components in application class itself.
119+
2. **Subcomponents**
120+
`Subcomponent`s were introduced in Dagger2. With `SubComponent` parent component just have declare getters of all its children components. While dependent components can have 1 or more parents `SubComponent` can only have one. A `SubComponent` has access to all dependancies supplied by its parent.
121+
122+
In generated code Dependent components access dependancies via an interface while Subcomponents are generated as inner classes of parent components so that they can directly access dependencies available in parent components. Dependent components can be used when components need more decoupling between then for example, different library modules in single project. SubComponents should be used when there is tight coupling between components for example different screens within an app.
123+
124+
Movie example make use of `@SubComponent` to propagate dependancies in individual screens. Lets start by creating module
125+
126+
```java
127+
@Module
128+
public class MovieModule {
129+
130+
private final Contracts.View movieView;
131+
132+
public MovieModule(Contracts.View movieView) {
133+
this.movieView = movieView;
134+
}
135+
136+
@Provides
137+
@MovieScope
138+
MovieListPresenter provideMovieListPresenter(ApiService apiService) {
139+
return new MovieListPresenter(movieView, apiService);
140+
}
141+
}
142+
```
143+
Note that how `MovieListPresenter` needs `ApiService` here. `ApiService` will be injected by `Dagger` on the fly, since, its coming from `AppComponent` we need not to worry about its initialization.
144+
145+
There is just one interesting new addition here, `@MovieScope`. `@MovieScope` is a custom scope which works just like `@Singleton` but has smaller lifecycle. In `Dagger2` one can define his custom scope in which created objects should live, `MovieScope` is one such custom scope. A `SubComponent` cannot have same scope as its parent hence we need to define a custom scope for our `SubComponent`. We can do so by using `@Scope` annotation. Lets have a look at definition of `MovieScope`, Its pretty straight.
146+
```java
147+
@Scope
148+
public @interface MovieScope {
149+
}
150+
```
151+
152+
Next is our `MovieComponent`
153+
```java
154+
@MovieScope
155+
@Subcomponent(modules = {MovieModule.class})
156+
public interface MovieComponent {
157+
void inject(MovieListFragment movieListFragment);
158+
}
159+
```
160+
161+
Notice that I have used `@SubComponent` instead of `@Component` annotation. `AppComponent` now needs to know that it has one `SubComponent` so we add a new line of code in `AppComponent`'s definition
162+
163+
```java
164+
MovieComponent newMovieComponent(MovieModule movieModule);
165+
```
166+
167+
Thats it, we have our `SubComponent` ready to supply dependencies to fragment and presenter.
168+
169+
### Get Set Inject Again! 😃
170+
In fragment's `onCreate` write this code
171+
```java
172+
((BaseApplication)getActivity().getApplication())
173+
.getAppComponent()
174+
.newMovieComponent(new MovieModule(this))
175+
.inject(this);
176+
```
177+
and declare your dependencies as field variables like this
178+
179+
```java
180+
@Inject
181+
ApiService apiService;
182+
183+
@Inject
184+
MovieListPresenter presenter;
185+
```
186+
187+
`@Inject` annotation will let `Dagger2` know what are the dependencies that this object needs and will initialize them automatically!
188+
189+
That is it, this is how dagger makes dependency injection easier. This code also looks pretty clean and readable. There is also movie detail screen here which I am leaving for you to explore because its pretty similar to what we have done for movie list.
190+
191+
You can find complete source code on [github here](https://github.com/abhishekBansal/android-mvp-retrofit2-dagger2-rxjava2-testing)
192+
193+
If you have any suggestions, let me know in comments!
194+
195+
Happy Coding!

assets/images/dagger-schema.png

101 KB
Loading

assets/images/movie-detail.png

214 KB
Loading

assets/images/movie-list.png

281 KB
Loading

0 commit comments

Comments
 (0)