반응형
Hilt 정의 및 기본 설정
- 프로젝트에서 종속 항목에 대해서 수동으로 삽입하는 코드를 줄이는 Android용 종속성 주입 라이브러리
- 모든 클래스와 종속 항목을 수동으로 구성
- 컨테이너를 사용하여 종속 항목을 재사용 및 관리
- 모든 Android 클래스에 컨테이너 제공 및 수명 주기 자동 관리
Hilt 목표
공식 문서 기반 표현
- Android 앱을 위한 Dagger 관련 인프라 간소화
- 앱 간의 설정, 가독성 및 코드 공유를 용이하게 하기 위한 표준 구성요소 및 범위 세트 생성
- 테스트, 디버그 또는 출시와 같은 다양한 빌드 유형에 서로 다른 결합을 프로비저닝하는 쉬운 방법 제공
Hilt 설정
- 아래 환경은 Kotlin Domain Specific Language 기반으로 작성
Project Root build.gradle
plugins {
...
id("com.google.dagger.hilt.android") version "2.44" apply false
}
Module build.gradle
plugins {
...
kotlin("kapt")
id("com.google.dagger.hilt.android")
}
dependencies {
...
implementation("com.google.dagger:hilt-android:2.44")
kapt("com.google.dagger:hilt-android-compiler:2.44")
}
// Allow references to generated code
kapt {
correctErrorTypes = true
}
Hilt 알아보기
Hilt application class
- Hilt를 사용하는 모든 앱은 어노테이션이 포함된 Application class가 있어야 함
- @HiltAndroidApp
- 해당 어노테이션은 Application의 기본 클래스와 Hilt의 코드 생성을 트리거
- 서브 라이브러리 모듈에서 hilt를 사용할 경우에도 메인 어플리케이션 모듈에서 지정해줘야 함
더보기
해당 부분때문에 Unity - Android 연결을 할 경우 Koin으로 DI 라이브러리를 바꾸는 것을 추천 Unity에 Android 라이브러리를 넣는 경우 Hilt를 쓰려고 해도 Unity에 Android Application 모듈이 존재하기 때문에 @HiltAndroidApp 어노테이션이 부착된 클래스를 AndroidManifest에 주입해도 메인 모듈에 존재하지 않기 때문에 에러 발생
@HiltAndroidApp
class CitytexiApplication : Application() { ... }
<application
android:name=".CitytexiApplication"
...
>
<!-- ... -->
</application>
Android Class 종속 항목 주입
- 어노테이션을 이용해서 다른 Android 클래스에 종속 항목을 제공할 수 있음
- @AndroidEntryPoint
- 해당 어노테이션을 사용할 수 있는 항목
- Application → @HiltAndroidApp을 사용
- ViewModel → @HiltViewModel을 사용
- Acitivity → ComponentActivity가 부모인 경우로 한정
- Fragment
- View
- Service
- BroadcastReceiver
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter
...
}
@HiltViewModel
class ExampleViewModel
@Inject
constructor(
) : ViewModel() {
...
}
- @Inject
- 구성요소에서 종속 항목을 가져오려면 사용하여 주입
Component hierarchy
- 컴포넌트 계층 구조에서 하위의 구성요소에 접근할 수 있음
Android Component | Default Binding |
SingletonComponent | Application |
ActivityRetainedComponent | Application |
ViewModelComponent | SavedStateHandle |
ActivityComponent | Activity Application |
FragmentComponent | Application, Activity, Fragment |
ViewComponent | Application, Activity, View |
ViewWithFragmentComponent | Application, Activity, Fragment, View |
ServiceComponent | Service Application |
class AnalyticsServiceImpl @Inject constructor(
@ApplicationContext context: Context
) : AnalyticsService { ... }
class AnalyticsAdapter @Inject constructor(
@ActivityContext context: Context
) { ... }
Hilt가 지원하지 않는 class에 종속성 주입
- Hilt과 관리하는 객체의 그래프에 처음 진입하는 지점을 표기하는 어노테이션을 이용하여 주입
- @EntryPoint
- 추가로 어노테이션을 지정하여 컴포넌트 지정
class ExampleContentProvider : ContentProvider() {
@EntryPoint
@InstallIn(SingletonComponent::class)
interface ExampleContentProviderEntryPoint {
fun analyticsService(): AnalyticsService
}
...
}
Constructor Injection
interface Operator {
fun calculation(a: Int, b: Int): Int
}
class SumOperator : Operator {
override fun calculation(a: Int, b: Int): Int = a + b
}
class Calculator
@Inject
constructor(
private val operator: Operator
) {
fun cal() {
operator.calculation(100, 200)
}
}
- 위 코드는 interface를 inject하는 액션에 의해서 에러가 나게 됨
Provides를 이용하여 해결
@Module
@InstallIn(SingletonComponent::class)
class CalModule {
@Provides
fun getNumberA(): Int = 100
@Provides
fun sumOperator(a: Int): Operator = SumOperator(a)
}
interface Operator {
fun calculation(): Int
}
class SumOperator(
private val a: Int
) : Operator {
override fun calculation(): Int = a + 200
}
class Calculator
@Inject
constructor(
private val operator: Operator
) {
fun cal() {
operator.calculation()
}
}
- Module class를 생성하고 SingletonComponent을 통해 binding 지정
- 해당 방법으로 Room, Retrofit, OkHttp 등 다양한 Android 라이브러리도 주입 가능
Binds를 이용한 해결
@Module
@InstallIn(SingletonComponent::class)
abstract class TestModule {
@ActivityScoped
@Binds
abstract fun bindInterfaceOperator(sumOperator: SumOperator): Operator
}
- 해당 방법은 외부 라이브러리에 대해서는 동작하지 않음
같은 객체 타입 주입
- 어노테이션 구현하고 해당 어노테이션을 이용하여 구별 가능
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class NumberA
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class NumberB
@NumberA
@Provides
fun provideNumberA(): Int = 100
@NumberB
@Provides
fun provideNumberB(): Int = 200
}
ViewModel 주입
- @AndroidEntryPoint 어노테이션이 붙은 Activity나 Fragment에 주입 가능
- Compose의 경우 HiltViewModel을 이용하여 주입 가능
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var analyticsHelper: AnalyticsHelper
@Inject
lateinit var networkMonitor: NetworkMonitor
private val viewModel: MainActivityViewModel by viewModels()
// ...
}
// or
@Composable
internal fun OnBoardingRoute(
navigateToHome: (NavOptions) -> Unit,
viewModel: OnBoardingViewModel = hiltViewModel(),
) {
OnBoardingScreen {}
}
Reference
https://developer.android.com/training/dependency-injection/hilt-android#kts
반응형