[android] Hilt

sundays·2022년 11월 22일
0

android

목록 보기
4/18

안녕하세요 이번에는 안드로이드에서 의존성 주입을 적용할때 사용하는 Hilt를 알아보겠습니다. viewpager 를 사용하려고 보니 android 에서 제공하는 참고 프로젝트에서는 hilt를 사용해주어 의존성 주입을 하고 있더라구요. 의존성 주입은 보통 수명주기 및 런타임 성능에 중요한 역할을 하기 때문에 사용하는 방법에 있어서 중요하다고 할 수 있습니다.

Hilt

  • 종속 항목 수동 삽입을 실행하는 상용구를 줄이는 android 종속 항목 삽입 라이브러리
  • 프로젝트의 모든 android 클래스에 컨테이너를 제공하고 수명 주기를 자동으로 관리함으로서 애플리케이션에 DI를 사용합니다
  • 컴파일 시간 정확성, 런타임 성능, 확장성 android studio 지원을 위해 dagger기반으로 빌드 되어있습니다.
  • Hilt는 현재 다음 Android 클래스를 지원합니다.
    • Application(@HiltAndroidApp을 사용하여)
    • ViewModel(@HiltViewModel을 사용하여)
    • Activity
    • Fragment
    • View
    • Service
    • BroadcastReceiver

1. build.gradle (ROOT)

plugins {
  ...
  id("com.google.dagger.hilt.android") version "2.44" apply false
}

2. build.gradle (APP)

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
}

3. Hilt Application Class

@HiltAndroidApp
class ExampleApplication : Application() { ... }
  • Hilt를 사용하기 위해서는 @HiltAndroidApp 주석이 지정된 Application Class를 포함해야 합니다
  • Application 기본 클래스를 비롯하여 Hilt의 코드 생성을 트리거 해주는 애플리케이션 수준 종속 항목 컨테이너 역할을 합니다.
  • Hilt 구성요소는 Application 객체의 수명 주기에 연결되어 종속 항목을 제공합니다
  • 앱의 상위 구성요소이기 때문에 다른 종속항목에 액세스 가능 합니다

4. Android Class

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }
  • android 클래스에 @AndroidEntryPoint로 주석을 지정하면 이 클래스에 종속된 android 클래스에도 주석을 지정해야 합니다
  • AppCompatActivity 같은 ComponentActivity를 확장하는 역할을 지원합니다
  • androidx.fragment를 확장하는 프래그먼트만 지원합니다
  • android 클래스에 관한 개별 hilt 구성 요소를 생성합니다
  • 구성 요소 계층 구조에서 설명된 대로 해당하는 상위 클래스에서 종속 항목을 받아올 수 있습니다.

4.1. 종속 항목

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}
  • @Inject 주석으로 사용할 클래스에 인스턴스를 제공

5. Hilt 결합 정의

  • 필드 삽입을 실행시에는 구성요소에 필요한 종속 항목의 인스턴스를 제공하는 방법으로 결합을 사용합니다.
  • 특정 유형의 인스턴스를 종속항목으로 제공하는 데 필요한 요소가 포함됩니다.

5.1. 생성자 삽입

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }
  • 주석이 지정된 클래스(AnalyticsService) 생성자의 매개변수는 그 클래스(AnalyticsAdapter)의 종속 항목입니다

5.2. Hilt 모듈

  • 생성자를 삽입할 수 없는 상황에 사용한다 (인터페이스 생성자 삽입시 또는 외부 라이브러리의 클래스 사용시)
  • @Module로 주석이 지정된 클래스
  • @InstallIn 주석을 지정하여 각 모듈을 사용하거나 설치할 android 클래스를 Hilt에 알려야 합니다
  • Gradle 모듈과 다릅니다.

5.2.1. @Binds를 사용하여 인터페이스 인스턴스 삽입

  • AnalyticsService가 인터페이스라면 이 인터페이스를 생성자 삽입할 수 없습니다
  • @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) 주석을 지정합니다. 이 주석은 모든 종속 항목을 앱의 모든 활동에서 사용할 수 있음을 의미합니다

5.2.2. @Provides를 사용하여 인스턴스 삽입

  • 외부 라이브러리의 사용으로 클래스를 소유하지 않은 경우
  • 빌더 패턴으로 인스턴스를 생성해야 하는 경우
  • AnalyticsService 클래스를 직접 소유하지 않으면 Hilt 모듈 내에 함수를 생성하고 이 함수에 @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)
  }
}

5.2.3. 동일 유형에 대한 인스턴스 삽입

한정자는 특정 유형에 대해 여러 결합이 정의되어있을때 그 유형의 특정 결합을 식별하는데 사용합니다. 다음과 같이 @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()
  }
}

사용하게 될때에는 필드나 매개변수에서 한정자로 주석을 지정하여 특정 유형을 삽입합니다.

5.2.4. 사전 정의 한정자

@ApplicationContext@ActivityContext 한정자를 제공합니다.

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }

6. 구성 요소 범위

Hilt의 모든 결합은 범위가 지정되지 않고 결합을 요청할 때마다 Hilt는 필요한 유형의 새 인스턴스를 생성합니다. (위의 ExampleActivity와 같이 AnalyticsAdapter를 제공할 때마다 AnalyticsAdapter의 새 인스턴스를 제공)

하지만 Hilt는 결합을 특정 구성 요소로 범위 지정할 수도 있습니다. Hilt 는 결합의 범위가 지정된 구성 요소의 인스턴스마다 한번만 범위가 지정된 결합을 생성하여 모든 요청이 동일한 인스턴스를 공유 합니다.

@ActivityScoped
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

@ActivityScoped 를 사용하여 AnalyticsAdapter의 범위를 ActivityComponent로 지정하면 Hilt는 해당 Activity의 수명 주기 동안 동일한 AnalyticsAdapter 인스턴스를 제공합니다.
각각 생성된 구성 요소의 범위와 클래스는 다음 과 같습니다.

7. 구성 요소 계층 구조

구성 요소에 module을 설치하면 이 구성요소는 다른 결합 또는 계층 구조의 하위 구성요소의 다른 결합 종속항목으로 설치된 module의 결합에 액세스할 수 있습니다.

8. Hilt가 지원하지 않는 클래스 종속 항목 삽입

ContentProvider와 같은 것을 직접 지원하지 않습니다. contentProvider가 Hilt를 사용하여 일부 종속 항목을 가져오게 하려면 @EntryPoint로 주석이 지정된 인터페이스와 한정자를 포함하도록 하고 @InstallIn을 추가하여 진입점을 설치할 구성 요소를 지정해야 합니다

class ExampleContentProvider : ContentProvider() {

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface ExampleContentProviderEntryPoint {
    fun analyticsService(): AnalyticsService
  }

  ...
}

Reference

profile
develop life

0개의 댓글