안녕하세요 이번에는 안드로이드에서 의존성 주입을 적용할때 사용하는 Hilt를 알아보겠습니다. viewpager 를 사용하려고 보니 android 에서 제공하는 참고 프로젝트에서는 hilt를 사용해주어 의존성 주입을 하고 있더라구요. 의존성 주입은 보통 수명주기 및 런타임 성능에 중요한 역할을 하기 때문에 사용하는 방법에 있어서 중요하다고 할 수 있습니다.
Application
(@HiltAndroidApp을 사용하여)ViewModel
(@HiltViewModel을 사용하여)Activity
Fragment
View
Service
BroadcastReceiver
plugins {
...
id("com.google.dagger.hilt.android") version "2.44" apply false
}
plugins {
kotlin("kapt")
id("com.google.dagger.hilt.android")
}
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
}
@HiltAndroidApp
class ExampleApplication : Application() { ... }
@HiltAndroidApp
주석이 지정된 Application Class를 포함해야 합니다@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }
@AndroidEntryPoint
로 주석을 지정하면 이 클래스에 종속된 android 클래스에도 주석을 지정해야 합니다AppCompatActivity
같은 ComponentActivity
를 확장하는 역할을 지원합니다androidx.fragment
를 확장하는 프래그먼트만 지원합니다@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter
...
}
@Inject
주석으로 사용할 클래스에 인스턴스를 제공class AnalyticsAdapter @Inject constructor(
private val service: AnalyticsService
) { ... }
@Module
로 주석이 지정된 클래스 @InstallIn
주석을 지정하여 각 모듈을 사용하거나 설치할 android 클래스를 Hilt에 알려야 합니다@Binds
는 인터페이스의 인스턴스를 제공해야 할 때 사용할 구현을 Hilt에 알려줍니다interface AnalyticsService {
fun analyticsMethods()
}
// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
...
) : AnalyticsService { ... }
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
AnalyticsModule
의 종속 항목을 ExampleActivity
에 삽입하기 위해@InstallIn(ActivityComponent::class)
주석을 지정합니다. 이 주석은 모든 종속 항목을 앱의 모든 활동에서 사용할 수 있음을 의미합니다@Provides
주석을 지정하여 이 유형의 인스턴스를 제공하는 방법을 Hilt에 알릴 수 있습니다@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
// Potential dependencies of this type
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(AnalyticsService::class.java)
}
}
한정자는 특정 유형에 대해 여러 결합이 정의되어있을때 그 유형의 특정 결합을 식별하는데 사용합니다. 다음과 같이 @Binds 또는 @Provides 메서드에 주석을 지정하는데 사용할 한정자를 정의합니다.
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient
각 한정자와 일치하는 유형의 인스턴스를 제공하는 방법을 가지고 동일한 반환유형을 가지면서 다른 결합으로 메서드에 라벨을 지정할 수 있습니다
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@AuthInterceptorOkHttpClient
@Provides
fun provideAuthInterceptorOkHttpClient(
authInterceptor: AuthInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.build()
}
@OtherInterceptorOkHttpClient
@Provides
fun provideOtherInterceptorOkHttpClient(
otherInterceptor: OtherInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(otherInterceptor)
.build()
}
}
사용하게 될때에는 필드나 매개변수에서 한정자로 주석을 지정하여 특정 유형을 삽입합니다.
@ApplicationContext
및 @ActivityContext
한정자를 제공합니다.
class AnalyticsAdapter @Inject constructor(
@ActivityContext private val context: Context,
private val service: AnalyticsService
) { ... }
Hilt의 모든 결합은 범위가 지정되지 않고 결합을 요청할 때마다 Hilt는 필요한 유형의 새 인스턴스를 생성합니다. (위의 ExampleActivity와 같이 AnalyticsAdapter를 제공할 때마다 AnalyticsAdapter의 새 인스턴스를 제공)
하지만 Hilt는 결합을 특정 구성 요소로 범위 지정할 수도 있습니다. Hilt 는 결합의 범위가 지정된 구성 요소의 인스턴스마다 한번만 범위가 지정된 결합을 생성하여 모든 요청이 동일한 인스턴스를 공유 합니다.
@ActivityScoped
class AnalyticsAdapter @Inject constructor(
private val service: AnalyticsService
) { ... }
@ActivityScoped
를 사용하여 AnalyticsAdapter의 범위를 ActivityComponent로 지정하면 Hilt는 해당 Activity의 수명 주기 동안 동일한 AnalyticsAdapter 인스턴스를 제공합니다.
각각 생성된 구성 요소의 범위와 클래스는 다음 과 같습니다.
구성 요소에 module을 설치하면 이 구성요소는 다른 결합 또는 계층 구조의 하위 구성요소의 다른 결합 종속항목으로 설치된 module의 결합에 액세스할 수 있습니다.
ContentProvider와 같은 것을 직접 지원하지 않습니다. contentProvider가 Hilt를 사용하여 일부 종속 항목을 가져오게 하려면 @EntryPoint로 주석이 지정된 인터페이스와 한정자를 포함하도록 하고 @InstallIn을 추가하여 진입점을 설치할 구성 요소를 지정해야 합니다
class ExampleContentProvider : ContentProvider() {
@EntryPoint
@InstallIn(SingletonComponent::class)
interface ExampleContentProviderEntryPoint {
fun analyticsService(): AnalyticsService
}
...
}