[안드로이드] Hilt

🔥 Hilt


✨ Hilt 사용의 장점


✨ Hilt 사용을 위한 환경 구축

1️⃣ 프로젝트의 루트 build.gradle 파일에 hilt-android-gradle-plugin 추가

buildscript {
    ...
    ext.hilt_version = '2.33-beta'
    dependencies {
        ...
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
    }
}


2️⃣ app/build.gradle 파일에 apply plugindependencies 추가

android { … }

dependencies { implementation “com.google.dagger:hilt-android:$hilt_version” kapt “com.google.dagger:hilt-compiler:$hilt_version” }

<br>

#### 3️⃣ `app/build.gradle`에 Java 8 `compileOptions` 추가
```xml
android {
    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}



✨ Hilt 애플리케이션 클래스 생성


✨ Android 클래스에 종속성 삽입

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

    @Inject lateinit var analytics: AnalyticsAdapter
    ...
}



✨ Hilt binding 정의


✨ Hilt modules

✔️ @Binds를 사용하여 인터페이스 인스턴스 주입

// 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 }

* `Hilt module`인 `AnalyticsModule`은 `Hilt`가 `ExampleActivity`로 종속성을 주입하도록 하기 위해 `@InstallIn(ActivityComponent::class)` 어노테이션으로 처리 되었다.
* `@InstallIn(ActivityComponent::class)` 어노테이션은 `AnalyticsModule`의 모든 종속성을 앱의 모든 액티비티에서 사용할 수 있음을 의미한다.
<br>

#### ✔️ `@Provides`를 사용하여 인스턴스 주입
* 인터페이스는 타입을 constructor-inject 할 수 없는 유일한 경우가 아니다.
* constructor-injection은 외부 라이브러리(`Retrofit`, `Room` 등)의 클래스 등과 같은 소유하고 있지 않은 클래스 및 빌더 패턴으로 생성된 인스턴스에서도 불가능하다.
* 이러한 경우, Hilt module 내부에 함수를 만들고 해당 함수를 `@Provides` 어노테이션으로 처리하여 타입 인스턴스를 제공하는 방법을 `Hilt`에 알릴 수 있다.
* `@Provides` 어노테이션이 있는 함수는 다음의 정보를 `Hilt`에 알려준다
 * 함수의 리턴타입은 함수가 인스턴스를 제공하는 타입을 `Hilt`에 알려준다.
 * 함수의 파라미터는 타입의 dependencies를 `Hilt`에 알려준다.
 * 함수의 바디는 타입의 인스턴스를 제공하는 방법을 `Hilt`에 알려준다. 타입의 인스턴스를 제공할 필요가 있을 때마다 `Hilt`는 함수의 바디를 실행한다.
```kotlin
@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)
    }
}


✔️ 동일한 타입에 대해 여러 바인딩 제공

@Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient

<br>

* 두 모듈은 같은 리턴타입을 지니고 있지만 qualifier가 두 모듈을 다른 바인딩으로 지정하고 있다.
```kotlin
@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()
    }
}


// As a dependency of a constructor-injected class. class ExampleServiceImpl @Inject constructor( @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient ) : …

// At field injection. @AndroidEntryPoint class ExampleActivity: AppCompatActivity() {

@AuthInterceptorOkHttpClient
@Inject lateinit var okHttpClient: OkHttpClient } ``` <br>

✔️ 사전 정의된 Hilt qualifier


✨ Android 클래스용으로 생성된 컴포넌트

Hilt component Inject for
SingletonComponent Application
ActivityRetainedComponent N/A
ViewModelComponent ViewModel
Activityomponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View annotated with @WithFragmentBindings
ServiceComponent Service

✔️ 컴포넌트 생명주기

|Generated component|Created at|Destroyed at| |——————:|:——–:|:———-:| |SingletonComponent|Application#onCreate()|Application#onDestroy()| |ActivityRetainedComponent|Activity#onCreate()|Activity#onDestroy()| |ViewModelComponent|ViewModel created|ViewModel destroyed| |Activityomponent|Activity#onCreate()|Activity#onDestroy()| |FragmentComponent|Fragment#onAttach()|Fragment#onDestroy()| |ViewComponent|View#super()|View destroyed| |ViewWithFragmentComponent|View#super()|View destroyed| |ServiceComponent|Service#onCreate()|Service#onDestroy()|

✔️ 컴포넌트 Scope

|Android class|Generated component|Scope| |————:|:—————–:|:—-| |Application|SingletonComponent|@Singleton| |Activity|ActivityRetainedComponent|@ActivityRetainedScoped| |ViewModel|ViewModelComponent|@ViewModelScoped| |Activity|ActivityComponent|@ActivityScoped| |Fragment|FragmentComponent|@FragmentScoped| |View|ViewComponent|@ViewScoped| |View annotated with @WithFragmentBindings|ViewWithFragmentComponent|@ViewScoped| |Service|ServiceComponent|@ServiceScoped|

// If you don’t own AnalyticsService. @Module @InstallIn(SingletonComponent::class) object AnalyticsModule {

@Singleton
@Provides
fun provideAnalyticsService(): AnalyticsService {
    return Retrofit.Builder()
             .baseUrl("https://example.com")
             .build()
             .create(AnalyticsService::class.java)
} } ``` <br>

✔️ 컴포넌트 Hierarchy

✔️ 컴포넌트 기본 바인딩

|Android component|Default bindings| |—————-:|:—————| |SingletonComponent|Application| |ActivityRetainedComponent|Application| |ViewModelComponent|SavedStateHandle| |ActivityComponent|Application, Activity| |FragmentComponent|Application, Activity, Fragment| |ViewComponent|Application, Activity, View| |ViewWithFragmentComponent|Application, Activity, Fragment, View| |ServiceComponent|Application, Service|

// The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { … }

<br>

* activity context의 바인딩은 `@ActivityBinding` 어노테이션을 통해서도 가능하다.
```kotlin
class AnalyticsAdapter @Inject constructor(
    @ActivityContext context: Context
) { ... }

// The Activity binding is available without qualifiers.
class AnalyticsAdapter @Inject constructor(
    activity: FragmentActivity
) { ... }



✨ Hilt에서 지원하지 않는 클래스에 종속성 주입


📝 References