I have just learnt manual dependency injection, but I am trying out Hilt to handle these dependency injections.
I want to inject a ViewModel
into a Fragment
. The fragment is contained within an Activity
. Right now, I have added the annotations to Application
, Activity
, and Fragment
.
@HiltAndroidApp
class MovieCatalogueApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
...
}
@AndroidEntryPoint
class HomeFragment : Fragment() {
private lateinit var binding: FragHomeBinding
private val viewmodel: HomeViewModel by viewModels()
...
As can be seen, my HomeFragment
depends on HomeViewModel
. I have added a ViewModel injection as described here like so.
class HomeViewModel @ViewModelInject constructor(
private val movieRepository: MovieRepository,
private val showRepository: ShowRepository,
@Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
...
}
However, the ViewModel
requires two repositories. Right now, my MovieRepository
is like so.
class MovieRepository (private val movieApi: MovieService) {
...
}
In the above code, MovieService
will be created by Retrofit using the Retrofit.create(class)
method. The interface used to create MovieService
is like so.
interface MovieService {
...
}
To get my Retrofit instance, I am using the following code.
object RetrofitService {
...
private var _retrofit: Retrofit? = null
val retrofit: Retrofit
get() {
return when (_retrofit) {
null -> {
_retrofit = Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
_retrofit!!
}
else -> _retrofit!!
}
}
}
I am not too sure how I can inject the Retrofit into the Repository to be used by my ViewModel
later on. Could someone give me some pointers or step-by-step instructions on how to do this?
Apparently, it is not as hard as it seems.
You have to first define the binding information to Hilt. Binding information tells Hilt how to provide the instances of the dependency specified. Because MovieService
is created using a Retrofit (which is a 3rd-party class not created by yourself) using the builder pattern, you can't use the constructor injection and you have to instead use Hilt modules and the annotation @Provides
to tell Hilt about this binding information.
As described in the doc, the annotated function in the Hilt module you have created will supply the following information to Hilt so that Hilt can provide the instances of the dependency.
• The function return type tells Hilt what type the function provides instances of.
• The function parameters tell Hilt the dependencies of the corresponding type.
• The function body tells Hilt how to provide an instance of the corresponding type. Hilt executes the function body every time it needs to provide an instance of that type.
In the end, you only need to modify the MovieRepository
class, add a module for each repository, and annotate the function that tells Hilt how to provide the service instance created with Retrofit with @Provides
.
Code.
class MovieRepository @Inject constructor(
private val movieApi: MovieService
) {
...
}
interface MovieService {
...
}
@Module
@InstallIn(ActivityRetainedComponent::class)
object MovieModule {
@Provides
fun provideMovieService(): MovieService
= RetrofitService.retrofit.create(MovieService::class.java)
}
As you can see, the ActivityRetainedComponent
is referred in the @InstallIn
annotation because the Repository is to be injected to a ViewModel
. Each Android component is associated to different Hilt components.