In this article, I will go straight to explaining how to use the @Inject
, @Provides
and @Binds
annotations. So skip the explanation of Dependency Injection or the introduction to Hilt
. Looks like everyone already knows how to use it. Let’s fight!!!
overview
We have 3 commonly used annotations to inject objects in Hilt:
@Inject
: annotation used in the constructor of the class@Provides
: annatation used in Module@Binds
: another annatation also used in Module
The question is when to use these guys?
Inject
We use the @Inject
annotation in all constructors that we need to inject objects, from ViewModel
, Repository
to DataSource
. For example:
1 2 3 4 5 6 | <span class="token keyword">class</span> ProfileRepository <span class="token annotation builtin">@Inject</span> <span class="token keyword">constructor</span> <span class="token punctuation">(</span> <span class="token keyword">private</span> <span class="token keyword">val</span> profileDataSource <span class="token operator">:</span> ProfileDataSource <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">fun</span> <span class="token function">doSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
This makes it easier to inject ProfileRepository
into other classes, such as ViewModel
or UseCase
. However, we can only use this annotation to annotate the constructor of the classes we define ourselves.
Provides
So to overcome the above weakness, inject objects of classes that we do not define (eg Retrofit
, OkHttpClient
or Room
database), we come to @Provides
. First, we need to create a @Module
to contain the dependencies with the @Provides
annotation. For example:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token annotation builtin">@Module</span> <span class="token keyword">class</span> NetworkModule <span class="token punctuation">{</span> <span class="token annotation builtin">@Provides</span> <span class="token keyword">fun</span> <span class="token function">providesApiService</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> ApiService <span class="token operator">=</span> Retrofit <span class="token punctuation">.</span> <span class="token function">Builder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">addConverterFactory</span> <span class="token punctuation">(</span> GsonConverterFactory <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">baseUrl</span> <span class="token punctuation">(</span> BASE_URL <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> ApiService <span class="token operator">::</span> <span class="token keyword">class</span> <span class="token punctuation">.</span> java <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Because Retrofit
initializes the object not our code define, and also initializes according to the Builder pattern, we cannot use @Inject
annotation but must use @Provides
. Now, we can inject the object of the ApiService
interface anywhere.
Binds
For the interface, we cannot use @Inject
annotation, because it does not have a constructor function. However, if you have an interface that has only one implementation (a class that implements that interface), then you can use @Binds
to inject that interface. Injecting interfaces instead of classes is a good practice, making it easier to test.
Going back to the ProfileRepository
in the @Inject
section, we’ll turn it into an interface, and create a class that implements that interface. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">interface</span> ProfileRepository <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> ProfileRepositoryImpl <span class="token annotation builtin">@Inject</span> <span class="token keyword">constructor</span> <span class="token punctuation">(</span> <span class="token keyword">private</span> <span class="token keyword">val</span> profileDataSource <span class="token operator">:</span> ProfileDataSource <span class="token punctuation">)</span> <span class="token operator">:</span> ProfileRepository <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token annotation builtin">@Module</span> <span class="token annotation builtin">@InstallIn</span> <span class="token punctuation">(</span> SingletonComponent <span class="token operator">::</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> RepositoryModule <span class="token punctuation">{</span> <span class="token annotation builtin">@Singleton</span> <span class="token annotation builtin">@Binds</span> <span class="token keyword">abstract</span> <span class="token keyword">fun</span> <span class="token function">bindProfileRepository</span> <span class="token punctuation">(</span> profileRepository <span class="token operator">:</span> ProfileRepositoryImpl <span class="token punctuation">)</span> <span class="token operator">:</span> ProfileRepository <span class="token punctuation">}</span> <span class="token keyword">class</span> RegisterUseCase <span class="token annotation builtin">@Inject</span> <span class="token keyword">constructor</span> <span class="token punctuation">(</span> <span class="token keyword">private</span> <span class="token keyword">val</span> profileRepository <span class="token operator">:</span> ProfileRepository <span class="token punctuation">)</span> |
The advantage of using @Binds
instead of @Provides
is that it reduces the amount of code generated, like the Module Factory class. Here you can find yourself still using @Inject
, because the constructor function of ProfileRepositoryImpl
still needs some parameters.
summary
So to sum it up
- Use
@Inject
for your code - Use
@Provides
for 3rd party code - Use
@Binds
for the inject interface, reducing unnecessary code
Reference