이번 영상에서는 Hilt로 Dependency를 주입하기 위한 준비를 해 보겠습니다.
Hilt를 사용하기 위해서 Dependency를 추가합니다. 우선은 Project 레벨의 build.gradle에 플러그인을 추가합니다.
plugins {
+ id 'com.google.dagger.hilt.android' version '2.41' apply false
}
다음은 app 레벨의 build.gradle에 플러그인을 추가합니다. 그리고 kapt가 에러타입을 알아서 판단할 수 있도록 correctErrorTypes을 true로 설정합니다.
plugins {
+ id 'dagger.hilt.android.plugin'
}
+kapt {
+ correctErrorTypes true
+}
dependencies {
+ // Hilt
+ implementation 'com.google.dagger:hilt-android:2.41'
+ kapt 'com.google.dagger:hilt-compiler:2.41'
}
다음은 모든 Hilt component의 가장 상위 스코프가 되는 Application class를 작성합니다.
@HiltAndroidApp
class BookSearchApplication : Application() {
}
그리고 이 클래스를 AndroidManifest.xml에 등록합니다. 이제 프로젝트를 빌드하면 Hilt에 의해 자동으로 의존성 그래프 파일이 생성되는 것을 확인할 수 있습니다.
<application
+ android:name=".BookSearchApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
자 그럼 의존성을 하나씩 Hilt로 대체해보도록 하겠습니다. 우선은 di 폴더 아래에 AppModule 클래스를 만들고 내용을 다음과 같이 작성합니다. 앱 전체에서 사용할 모듈이므로 SingletonComponent에 설치하고 @Module 표시를 해 줍니다.
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
}
첫 타자는 Retrofit입니다. AppModule에 세 개의 메소드를 작성합니다.
object AppModule {
+ // Retrofit
+ @Singleton
+ @Provides
+ fun provideOkHttpClient(): OkHttpClient {
+ val httpLoggingInterceptor = HttpLoggingInterceptor()
+ .setLevel(HttpLoggingInterceptor.Level.BODY)
+ return OkHttpClient.Builder()
+ .addInterceptor(httpLoggingInterceptor)
+ .build()
+ }
+
+ @Singleton
+ @Provides
+ fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
+ Retrofit.Builder()
+ .addConverterFactory(MoshiConverterFactory.create())
+ .client(okHttpClient)
+ .baseUrl(BASE_URL)
+ .build()
+ }
+
+ @Singleton
+ @Provides
+ fun provideApiService(retrofit: Retrofit): BookSearchApi {
+ retrofit.create(BookSearchApi::class.java)
+ }
}
로깅에 사용할 OkHttpClient를 주입하는 provideOkHttpClient, Retrofit 객체를 작성하는 provideRetrofit, BookSearchApi 서비스 객체를 작성하는 provideApiService를 만드는데 내용은 RetrofitInstance 클래스에서 구성한 것과 동일합니다. 그러면 RetrofitInstance 클래스는 이제 필요가 없기 때문에 삭제합니다.
-object RetrofitInstance {
- private val okHttpClient: OkHttpClient by lazy {
- val httpLoggingInterceptor = HttpLoggingInterceptor()
- .setLevel(HttpLoggingInterceptor.Level.BODY)
- OkHttpClient.Builder()
- .addInterceptor(httpLoggingInterceptor)
- .build()
- }
-
- private val retrofit: Retrofit by lazy {
- Retrofit.Builder()
- .addConverterFactory(MoshiConverterFactory.create())
- .client(okHttpClient)
- .baseUrl(BASE_URL)
- .build()
- }
-
- val api: BookSearchApi by lazy {
- retrofit.create(BookSearchApi::class.java)
- }
-}
다음은 Room 의존성을 생성하겠습니다.
object AppModule {
+ // Room
+ @Singleton
+ @Provides
+ fun provideBookSearchDatabase(@ApplicationContext context: Context): BookSearchDatabase =
+ Room.databaseBuilder(
+ context.applicationContext,
+ BookSearchDatabase::class.java,
+ "favorite-books"
+ ).build()
}
BookSearchDatabase를 만드는 provideBookSearchDatabase 메소드를 작성합니다. @Singleton을 붙이면 BookSearchDatabase 객체가 싱글톤으로 생성되고, @Provides를 붙이면 앱 내 어디든 필요한 곳에 주입할 수 있게 됩니다. 그러면 BookSearchDatabase 내부에서 객체를 싱글톤으로 수동 생성하는 내용은 더 이상 필요가 없기 때문에 삭제합니다.
abstract class BookSearchDatabase : RoomDatabase() {
abstract fun getBookSearchDao(): BookSearchDao
- companion object {
- @Volatile
- private var INSTANCE: BookSearchDatabase? = null
-
- private fun buildDatabase(context: Context): BookSearchDatabase =
- Room.databaseBuilder(
- context.applicationContext,
- BookSearchDatabase::class.java,
- "favorite-books"
- ).build()
-
- fun getInstance(context: Context): BookSearchDatabase =
- INSTANCE ?: synchronized(this) {
- INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
- }
- }
}
다음은 DataStore 의존성을 작성합니다.
object AppModule {
+ // DataStore
+ @Singleton
+ @Provides
+ fun providePreferencesDataStore(@ApplicationContext context: Context): DataStore<Preferences> =
+ PreferenceDataStoreFactory.create(
+ produceFile = { context.preferencesDataStoreFile(DATASTORE_NAME) }
+ )
}
AppModule에 providePreferencesDataStore 메소드를 작성하고 PreferenceDataStoreFactory를 통해 싱글톤 객체를 생성합니다. 기존의 by preferencesDataStore 로 하던 객체 생성 작업을 그대로 정의해 주는 과정입니다. 객체 생성시 필요한 context는 @ApplicationContext를 통해 주입합니다.
다음은 WorkManager 의존성을 작성합니다. @ApplicationContext로 context를 주입받고 getInstance를 써서 싱글톤 객체를 만들면 됩니다.
object AppModule {
+ // WorkManager
+ @Singleton
+ @Provides
+ fun provideWorkManager(@ApplicationContext context: Context): WorkManager =
+ WorkManager.getInstance(context)
}
다음은 ViewModel에 주입할 BookSearchRepository를 제공하는 RepositoryModule을 작성합니다.
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Singleton
@Binds
abstract fun bindBookSearchRepository(
bookSearchRepositoryImpl: BookSearchRepositoryImpl,
): BookSearchRepository
}
BookSearchRepository는 인터페이스이기 때문에 @Bind를 사용해서 Hilt가 의존성 객체를 생성할 수 있도록 설정합니다.