|
| 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! |
0 commit comments