[Hilt] 기존 프로젝트에 Hilt 적용해보기
이전에 공부하며 다뤘던 연습 프로젝트인 Bus Schedule 앱에 Hilt를 적용시켜 보려고 한다.
작성했던 글을 토대로 Hilt 라이브러리를 불러오고 종속성 선언을 해주자.
https://small-stepping.tistory.com/1096
해당 프로젝트는 ksp를 쓰고 있었고 Hilt Alpha 버전을 지원하고 있지만, kapt로 바꾸어 진행하였다.
1. build.gradle.kts (Project)
buildscript {
extra.apply {
set("nav_version", "2.5.3")
set("room_version", "2.5.1")
}
}
plugins {
id("com.android.application") version "8.1.1" apply false
id("com.android.library") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "2.0.0" apply false //<-- Version Up to 2.0.0
id("com.google.dagger.hilt.android") version "2.51.1" apply false // <-- Add
id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" apply false //<-- Add
id("org.jetbrains.kotlin.kapt") version "2.0.0" apply false //<-- Add
}
2. build.gradle.kts (app)
plugins {
id("com.android.application")
id("com.google.dagger.hilt.android") //<-- Add
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose") //<-- Add
id("org.jetbrains.kotlin.kapt") //<-- Add
}
...
dependencies {
implementation(platform("androidx.compose:compose-bom:2024.06.00"))
implementation("androidx.activity:activity-compose:1.9.0")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3")
implementation("androidx.navigation:navigation-compose:${rootProject.extra["nav_version"]}")
// hilt
implementation("com.google.dagger:hilt-android:2.51.1") //<-- Add
kapt("com.google.dagger:hilt-compiler:2.51.1") //<-- Add
// room
implementation("androidx.room:room-ktx:${rootProject.extra["room_version"]}")
implementation("androidx.room:room-runtime:${rootProject.extra["room_version"]}")
kapt("androidx.room:room-compiler:${rootProject.extra["room_version"]}") //<-- Ksp to kapt
debugImplementation("androidx.compose.ui:ui-test-manifest")
debugImplementation("androidx.compose.ui:ui-tooling")
}
이후 DI 되어있는 곳 Application에 @HilstAndroidApp 어노테이션을 먼저 추가하며 내부에 있던 내용물을 지운다.
import android.app.Application
import com.example.busschedule.data.AppContainer
import com.example.busschedule.data.AppDataContainer
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp //<-- Add
class BusScheduleApplication: Application()
/* Delete
{
lateinit var container: AppContainer
override fun onCreate() {
super.onCreate()
container = AppDataContainer(this)
}
}
*/
Hilt를 사용하기 위한 것으로, 의존성 주입은 onCreate의 super.onCreate()에서 바이트 코드로 변한 뒤에 이루어진다.
내부 내용물인 AppDataContainer는 데이터베이스와 연결, ViewModel Factory에서 Repository를 사용하기 위한 코드이다.
이를 작동시키기 위해 다음과 같이 재구성하였다.
이제 데이터 베이스와 레포지트리의 의존성 주입을 위한 모듈을 만든다.
우선 데이터 베이스를 위해 다음과 같은 DataModule를 만든다.
import android.content.Context
import com.example.busschedule.data.BusScheduleDao
import com.example.busschedule.data.BusScheduleDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DataModule {
@Singleton
@Provides
fun provideBusScheduleDatabase(@ApplicationContext context: Context): BusScheduleDatabase = BusScheduleDatabase.getDatabase(context)
@Singleton
@Provides
fun provideBusScheduleDao(database: BusScheduleDatabase): BusScheduleDao = database.busScheduleDao()
}
- @InstallIn - Hilt가 생성하는 DI 컨테이너에 어떤 모듈을 사용할지 가르킨다.
- @Module - InstallIn으로 설정한 객체들의 모음
- @Provides - 의존성 주입
- @ApplicationContext - applicationContext를 제공
- @HiltAndroidApp을 통해 ApplicationComponent가 생성된다.
- @InstallIn을 통해 해당 컴포넌트의 모듈이 생성된다.
- Activity에 @AndroidEntryPoint를 통해 ActivityComponent가 생성된다.
- Repository를 주입받는다.
데이터 모듈에서 SingletonComponent로 생성했기에 @Singleton을 설정해줘야 한다.
Repository의 구현부에 다음과 같이 @Singleton, @Inject constructor를 추가한다.
package com.example.busschedule.data
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class OfflineBusScheduleRepository @Inject constructor(private val busScheduleDao: BusScheduleDao): BusScheduleRepository {
...
}
ViewModel에는 @HiltViewModel, @Inject constructor를 추가한다.
Hilt의 의존성 주입은 @Inject를 통해 주입이 가능하기 때문이다.
import androidx.lifecycle.ViewModel
import com.example.busschedule.data.BusSchedule
import com.example.busschedule.data.BusScheduleRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
@HiltViewModel
class BusScheduleViewModel @Inject constructor(private val repository: BusScheduleRepository): ViewModel() {
// Get example bus schedule
fun getFullSchedule(): Flow<List<BusSchedule>> = repository.getAllSchedule()
// Get example bus schedule by stop
fun getScheduleFor(stopName: String): Flow<List<BusSchedule>> = repository.getStopByName(stopName)
}
이후 다음과 같은 RepositoryModule를 만든다.
import com.example.busschedule.data.BusScheduleRepository
import com.example.busschedule.data.OfflineBusScheduleRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
@Module
@InstallIn(ViewModelComponent::class)
interface RepositoryModule {
@Binds
fun bindRepository(repository: OfflineBusScheduleRepository): BusScheduleRepository
}
위에서 다 작업을 했다면 이제 Activity에서 사용할 수 있으므로 MainActivity에 @AndroidEntryPoint를 추가한다.
(만약 ContentProvider, DFM, Dagger를 사용하지 않는 서드파티 라이브러리들은 @EntryPoint를 사용한다.)
MainActivity의 하위 Fragment가 있다면 마찬가지로 @AndroidEntryPoint를 추가해줘야한다. 외에도 Service, BroadcastReceiver도 사용 가능하다.
JetpackCompose의 요소 fun ~~에 추가하면 상위 수준에 추가하라고 에러가 발생한다.
생성자 외에도 변수에 @Inject를 할 수 있으나 lateinit으로 설정해야하며 private도 사용할 수 없다.
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.example.busschedule.ui.BusScheduleApp
import com.example.busschedule.ui.theme.BusScheduleTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BusScheduleTheme {
BusScheduleApp()
}
}
}
}
https://developer.android.com/codelabs/android-hilt?hl=ko#0
https://readystory.tistory.com/210
https://stackoverflow.com/questions/64820394/using-dagger-hilt-in-repository-class
https://moonbari.tistory.com/99
https://it-of-fortune.tistory.com/26