I'm trying to understand HILT and I'm creating a sample project, I'm used to creating modules and components, but now with a hilt, there's the InstallIn() and perhaps I have to play with them to do the same as before. For instance, I'm used to creating a module per feature as :
LoginModule (contains: ViewModel, activity/fragment, data source, use case, etc...) ProductModule (contains: ViewModel, activity/fragment, data source, use case, etc...)
From now I've created a NetworkModule with hilt :
@Module
@InstallIn(ApplicationComponent::class)
object NetworkModule {
@Provides
fun provideRetrofit(httpClient: OkHttpClient): Retrofit =
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(httpClient)
.build()
@Provides
fun provideOkHttpClient(
httpLoggingInterceptor: HttpLoggingInterceptor,
@ErrorInterceptorOkHttpClient errorInterceptor: Interceptor,
): OkHttpClient = OkHttpClient()
.newBuilder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(errorInterceptor)
.build()
@ErrorInterceptorOkHttpClient
@Provides
fun provideErrorInterceptor(): Interceptor = ErrorInterceptor()
@Provides
fun provideHttpLogginInterceptor(): HttpLoggingInterceptor {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = when {
BuildConfig.DEBUG -> HttpLoggingInterceptor.Level.BODY
else -> HttpLoggingInterceptor.Level.NONE
}
return loggingInterceptor
}
@Provides
fun provideMyService(retrofit: Retrofit): MyService =
retrofit.create(MyService::class.java)
}
And then I have the feature or let's say part of the app that is the home where I want to show some things from now a List, and I've created this :
@Module
@InstallIn(FragmentComponent::class)
object MainActivityModule {
@Provides
fun proviceDataSource(
myService: MyService,
myMapper: MyMapper,
): MyDatasource = MyDatasourceImpl(myService, myMapper)
@Provides
fun provicesMyMapper(): MyMapper = MapperImpl()
}
And in my Activity, I've added the
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
And in Fragment, I don't have anything maybe this is the thing that I'm doing wrong?
And when to use the ApplicationComponent SingletonComponent , etc?
Since I was using dagger (not dagger-android) I had a ComponentFactory where I had all of this Components as (LoginComponent, GoogleMapComponent, ReferalCodeComponent) all of those were a feature (talking about multi-module project) and there I was creating the factory of these components, how's the way of doing this now with Hilt?
When using multiple modules, if I follow my idea, I will define multiple modules in a library and provide them to the interface module for use.
Your question is how to use the factory method you defined in the interface module? I don't understand your question very well on this point.
If you use the MVVM architecture, you can inject these components into the ViewModel . The Flow provided by these components (such as LoginComponent) can be accessed in the ViewModel. Of course, the parameters injected through the ViewModel can also easily access the corresponding methods of the components you provide.
Below is the ViewModel in my Demo:
@HiltViewModel
class NickNameViewModel @Inject constructor(
private val repo: DataRepo,
application: Application,
): AndroidViewModel(application) {
fun updateUserNickName(
userId: String,
userKey: String,
userNickName: String,
resultCallBack: (Boolean, String) -> Unit = {_, _ -> },
) {
viewModelScope.launch {
repo.updateUserInfo(
userId = userId,
userKey = userKey,
userNickName = userNickName,
).collect {
//.....
}
}
}
}
}
In the above ViewModel, the DetaRepo component is injected into the ViewModel through construction injection.
The method provided in DataRepo can be easily accessed in the method updateUserNickName.
Below is the code of ViewModel in Fragment:
@AndroidEntryPoint
class NickNameModifyFragment: Fragment(R.layout.nick_name_modify_fragment) {
companion object {
const val nickNickNameResult = "nickNickNameResult"
}
private var _binding: NickNameModifyFragmentBinding? = null
private val viewModel by viewModels<NickNameViewModel>()
}
It should be noted that if Hilt is in Fragment's ViewModel, you still need to add the @AndroidEntryPoint annotation to the attach Activity.
Hope to bring you good luck.
If your using SingletonComponent then with the @Provides annotation you should also required to use @singleton annotation
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton @Provides
fun provideServiceGenerator(
context: Context
) = ServiceGenerator(context)
}
similarly For FragmentComponent you need annotate with @Provides @FragmentScoped
as I read you mentioned UseCase and other stuff u must check out hilt viewModel because it will auto generate a lot boilerplate code for you and handles dependency according to activity or fragment lifecycle
@Module
@InstallIn(ViewModelComponent::class)
object ViewModelModule {
//repo
@Provides
@ViewModelScoped
fun providesTicketsRepo(
checkOutApi: CheckOutApi
)= TicketsRepo(checkOutApi)
}
@HiltViewModel
class TicketsAndCouponViewModel
@Inject constructor(
val ticketsRepo: TicketsRepo
) : ViewModel() { /****/}
inside fragment and activity
val viewModel by viewModels<TicketsAndCouponViewModel>()