Hilt를 사용한 종속 항목 삽입

Hilt는 프로젝트에서 종속 항목 수동 삽입을 실행하는 상용구를 줄이는 Android용 종속 항목 삽입 라이브러리입니다. 종속 항목 수동 삽입을 실행하려면 모든 클래스와 종속 항목을 수동으로 구성하고 컨테이너를 사용하여 종속 항목을 재사용 및 관리해야 합니다.

Hilt는 프로젝트의 모든 Android 클래스에 컨테이너를 제공하고 수명 주기를 자동으로 관리함으로써 애플리케이션에서 DI를 사용하는 표준 방법을 제공합니다. Hilt는 Dagger가 제공하는 컴파일 시간 정확성, 런타임 성능, 확장성 및 Android 스튜디오 지원의 이점을 누리기 위해 인기 있는 DI 라이브러리인 Dagger를 기반으로 빌드되었습니다. 자세한 내용은 Hilt 및 Dagger를 참조하세요.

이 가이드에서는 Hilt의 기본 개념 및 생성된 컨테이너에 관해 설명합니다. 또한 데모를 통해 기존 앱을 부트스트랩하여 Hilt를 사용하는 방법을 보여줍니다.

종속 항목 추가

먼저 hilt-android-gradle-plugin 플러그인을 프로젝트의 루트 build.gradle 파일에 추가합니다.

Groovy

plugins {
  ...
  id 'com.google.dagger.hilt.android' version '2.44' apply false
}

Kotlin

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

그런 다음, Gradle 플러그인을 적용하고 app/build.gradle 파일에 다음 종속 항목을 추가합니다.

Groovy

...
plugins {
  id 'kotlin-kapt'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.44"
  kapt "com.google.dagger:hilt-compiler:2.44"
}

// Allow references to generated code
kapt {
  correctErrorTypes true
}

Kotlin

plugins {
  id("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
}

Hilt는 자바 8 기능을 사용합니다. 프로젝트에서 자바 8을 사용 설정하려면 app/build.gradle 파일에 다음을 추가합니다.

Groovy

android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Kotlin

android {
  ...
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
  }
}

Hilt 애플리케이션 클래스

Hilt를 사용하는 모든 앱은 @HiltAndroidApp으로 주석이 지정된 Application 클래스를 포함해야 합니다.

@HiltAndroidApp은 애플리케이션 수준 종속 항목 컨테이너 역할을 하는 애플리케이션의 기본 클래스를 비롯하여 Hilt의 코드 생성을 트리거합니다.

Kotlin

@HiltAndroidApp
class ExampleApplication : Application() { ... }

자바

@HiltAndroidApp
public class ExampleApplication extends Application { ... }

생성된 이 Hilt 구성요소는 Application 객체의 수명 주기에 연결되며 이와 관련한 종속 항목을 제공합니다. 또한 이는 앱의 상위 구성요소이므로 다른 구성요소는 이 상위 구성요소에서 제공하는 종속 항목에 액세스할 수 있습니다.

Android 클래스에 종속 항목 삽입

Application 클래스에 Hilt를 설정하고 애플리케이션 수준 구성요소를 사용할 수 있게 되면 Hilt는 @AndroidEntryPoint 주석이 있는 다른 Android 클래스에 종속 항목을 제공할 수 있습니다.

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

자바

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity { ... }

Hilt는 현재 다음 Android 클래스를 지원합니다.

  • Application(@HiltAndroidApp을 사용하여)
  • ViewModel(@HiltViewModel을 사용하여)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Android 클래스에 @AndroidEntryPoint로 주석을 지정하면 이 클래스에 종속된 Android 클래스에도 주석을 지정해야 합니다. 예를 들어 프래그먼트에 주석을 지정하면 이 프래그먼트를 사용하는 활동에도 주석을 지정해야 합니다.

@AndroidEntryPoint는 프로젝트의 각 Android 클래스에 관한 개별 Hilt 구성요소를 생성합니다. 이러한 구성요소는 구성요소 계층 구조에 설명된 대로 해당하는 각 상위 클래스에서 종속 항목을 받을 수 있습니다.

구성요소에서 종속 항목을 가져오려면 다음과 같이 @Inject 주석을 사용하여 필드 삽입을 실행합니다.

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

자바

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Hilt가 삽입하는 클래스에는 삽입도 사용하는 다른 기본 클래스가 있을 수 있습니다. 이러한 클래스는 추상적인 경우 @AndroidEntryPoint 주석이 필요하지 않습니다.

Android 클래스가 삽입되는 수명 주기 콜백에 관한 자세한 내용은 구성요소 전체 기간을 참고하세요.

Hilt 결합 정의

필드 삽입을 실행하려면 Hilt가 해당 구성요소에서 필요한 종속 항목의 인스턴스를 제공하는 방법을 알아야 합니다. 결합에는 특정 유형의 인스턴스를 종속 항목으로 제공하는 데 필요한 정보가 포함됩니다.

Hilt에 결합 정보를 제공하는 한 가지 방법은 생성자 삽입입니다. 다음과 같이 클래스의 생성자에서 @Inject 주석을 사용하여 클래스의 인스턴스를 제공하는 방법을 Hilt에 알려줍니다.

Kotlin

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

자바

public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

주석이 지정된 클래스 생성자의 매개변수는 그 클래스의 종속 항목입니다. 이 예에서 AnalyticsAdapter에는 AnalyticsService가 종속 항목으로 있습니다. 따라서 Hilt는 AnalyticsService의 인스턴스를 제공하는 방법도 알아야 합니다.

Hilt 모듈

때로 유형을 생성자 삽입할 수 없는 상황도 있습니다. 이러한 상황은 여러 가지 이유로 인해 발생할 수 있습니다. 예를 들어 인터페이스를 생성자 삽입할 수 없습니다. 또한 외부 라이브러리의 클래스와 같이 소유하지 않은 유형도 생성자 삽입할 수 없습니다. 이럴 때는 Hilt 모듈을 사용하여 Hilt에 결합 정보를 제공할 수 있습니다.

Hilt 모듈은 @Module로 주석이 지정된 클래스입니다. Dagger 모듈과 마찬가지로 이 모듈은 특정 유형의 인스턴스를 제공하는 방법을 Hilt에 알려줍니다. 그러나 Dagger 모듈과는 달리, Hilt 모듈에 @InstallIn 주석을 지정하여 각 모듈을 사용하거나 설치할 Android 클래스를 Hilt에 알려야 합니다.

Hilt 모듈에 제공하는 종속 항목은 Hilt 모듈을 설치하는 Android 클래스와 연결되어 있는 모든 생성된 구성요소에서 사용할 수 있습니다.

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

AnalyticsService 예를 생각해 보세요. AnalyticsService가 인터페이스라면 이 인터페이스를 생성자 삽입할 수 없습니다. 대신 Hilt 모듈 내에 @Binds로 주석이 지정된 추상 함수를 생성하여 Hilt에 결합 정보를 제공합니다.

@Binds 주석은 인터페이스의 인스턴스를 제공해야 할 때 사용할 구현을 Hilt에 알려줍니다.

주석이 지정된 함수는 Hilt에 다음 정보를 제공합니다.

  • 함수 반환 유형은 함수가 어떤 인터페이스의 인스턴스를 제공하는지 Hilt에 알려줍니다.
  • 함수 매개변수는 제공할 구현을 Hilt에 알려줍니다.

Kotlin

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
}

자바

public interface AnalyticsService {
  void analyticsMethods();
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
public class AnalyticsServiceImpl implements AnalyticsService {
  ...
  @Inject
  AnalyticsServiceImpl(...) {
    ...
  }
}

@Module
@InstallIn(ActivityComponent.class)
public abstract class AnalyticsModule {

  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

Hilt가 AnalyticsModule의 종속 항목을 ExampleActivity에 삽입하기를 원하기 때문에 Hilt 모듈 AnalyticsModule@InstallIn(ActivityComponent.class) 주석을 지정합니다. 이 주석은 AnalyticsModule의 모든 종속 항목을 앱의 모든 활동에서 사용할 수 있음을 의미합니다.

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

유형을 생성자 삽입할 수 없는 것은 인터페이스만이 아닙니다. 클래스가 외부 라이브러리에서 제공되므로 클래스를 소유하지 않은 경우(Retrofit, OkHttpClient 또는 Room 데이터베이스와 같은 클래스) 또는 빌더 패턴으로 인스턴스를 생성해야 하는 경우에도 생성자 삽입이 불가능합니다.

이전 예를 생각해 보세요. AnalyticsService 클래스를 직접 소유하지 않으면 Hilt 모듈 내에 함수를 생성하고 이 함수에 @Provides 주석을 지정하여 이 유형의 인스턴스를 제공하는 방법을 Hilt에 알릴 수 있습니다.

주석이 달린 함수는 Hilt에 다음 정보를 제공합니다.

  • 함수 반환 유형은 함수가 어떤 유형의 인스턴스를 제공하는지 Hilt에 알려줍니다.
  • 함수 매개변수는 해당 유형의 종속 항목을 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)
  }
}

Java

@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    // Potential dependencies of this type
  ) {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

동일한 유형에 대해 여러 결합 제공

종속 항목과 동일한 유형의 다양한 구현을 제공하는 Hilt가 필요한 경우에는 Hilt에 여러 결합을 제공해야 합니다. 한정자를 사용하여 동일한 유형에 대해 여러 결합을 정의할 수 있습니다.

한정자는 특정 유형에 대해 여러 결합이 정의되어 있을 때 그 유형의 특정 결합을 식별하는 데 사용하는 주석입니다.

다음 예를 생각해 보세요. AnalyticsService 호출을 가로채야 한다면 인터셉터와 함께 OkHttpClient 객체를 사용할 수 있습니다. 다른 서비스에서는 호출을 다른 방식으로 가로채야 할 수도 있습니다. 이 경우에는 서로 다른 두 가지 OkHttpClient 구현을 제공하는 방법을 Hilt에 알려야 합니다.

먼저 다음과 같이 @Binds 또는 @Provides 메서드에 주석을 지정하는 데 사용할 한정자를 정의합니다.

Kotlin

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

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

자바

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface AuthInterceptorOkHttpClient {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface OtherInterceptorOkHttpClient {}

그런 다음, Hilt는 각 한정자와 일치하는 유형의 인스턴스를 제공하는 방법을 알아야 합니다. 이 경우에는 @Provides와 함께 Hilt 모듈을 사용할 수 있습니다. 두 메서드 모두 동일한 반환 유형을 갖지만 한정자는 다음과 같이 두 가지의 서로 다른 결합으로 메서드에 라벨을 지정합니다.

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()
  }
}

자바

@Module
@InstallIn(ActivityComponent.class)
public class NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideAuthInterceptorOkHttpClient(
    AuthInterceptor authInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(authInterceptor)
                   .build();
  }

  @OtherInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideOtherInterceptorOkHttpClient(
    OtherInterceptor otherInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(otherInterceptor)
                   .build();
  }
}

다음과 같이 필드 또는 매개변수에 해당 한정자로 주석을 지정하여 필요한 특정 유형을 삽입할 수 있습니다.

Kotlin

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .client(okHttpClient)
               .build()
               .create(AnalyticsService::class.java)
  }
}

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

자바

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    @AuthInterceptorOkHttpClient OkHttpClient okHttpClient
  ) {
      return new Retrofit.Builder()
                  .baseUrl("https://example.com")
                  .client(okHttpClient)
                  .build()
                  .create(AnalyticsService.class);
  }
}

// As a dependency of a constructor-injected class.
public class ExampleServiceImpl ... {

  private final OkHttpClient okHttpClient;

  @Inject
  ExampleServiceImpl(@AuthInterceptorOkHttpClient OkHttpClient okHttpClient) {
    this.okHttpClient = okHttpClient;
  }
}

// At field injection.
@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @AuthInterceptorOkHttpClient
  @Inject
  OkHttpClient okHttpClient;
  ...
}

한정자를 유형에 추가한다면 그 종속 항목을 제공하는 가능한 모든 방법에 한정자를 추가하는 것이 좋습니다. 기본 또는 일반 구현을 한정자 없이 그대로 두면 오류가 발생하기 쉬우며 Hilt가 잘못된 종속 항목을 삽입할 수 있습니다.

Hilt의 사전 정의된 한정자

Hilt는 몇 가지 사전 정의된 한정자를 제공합니다. 예를 들어 애플리케이션 또는 활동의 Context 클래스가 필요할 수 있으므로 Hilt는 @ApplicationContext@ActivityContext 한정자를 제공합니다.

예의 AnalyticsAdapter 클래스에 활동 컨텍스트가 필요하다고 가정해 보겠습니다. 다음 코드는 AnalyticsAdapter에 활동 컨텍스트를 제공하는 방법을 보여줍니다.

Kotlin

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

자바

public class AnalyticsAdapter {

  private final Context context;
  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(
    @ActivityContext Context context,
    AnalyticsService service
  ) {
    this.context = context;
    this.service = service;
  }
}

Hilt에서 사용 가능한 사전 정의된 다른 결합은 구성요소 기본 결합을 참고하세요.

Android 클래스용으로 생성된 구성요소

필드 삽입을 실행할 수 있는 각 Android 클래스마다 @InstallIn 주석에 참조할 수 있는 관련 Hilt 구성요소가 있습니다. 각 Hilt 구성요소는 해당 Android 클래스에 결합을 삽입해야 합니다.

이전 예에서는 Hilt 모듈에서 ActivityComponent를 사용하는 방법을 보여주었습니다.

Hilt는 다음 구성요소를 제공합니다.

Hilt 구성요소 인젝터 대상
SingletonComponent Application
ActivityRetainedComponent N/A
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent @WithFragmentBindings 주석이 지정된 View
ServiceComponent Service

구성요소 전체 기간

Hilt는 해당 Android 클래스의 수명 주기에 따라 생성된 구성요소 클래스의 인스턴스를 자동으로 만들고 제거합니다.

생성된 구성요소 생성 위치 소멸 위치
SingletonComponent Application#onCreate() Application 소멸됨
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel 생성됨 ViewModel 소멸됨
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View 소멸됨
ViewWithFragmentComponent View#super() View 소멸됨
ServiceComponent Service#onCreate() Service#onDestroy()

구성요소 범위

기본적으로 Hilt의 모든 결합은 범위가 지정되지 않습니다. 즉, 앱이 결합을 요청할 때마다 Hilt는 필요한 유형의 새 인스턴스를 생성합니다.

이 예에서 Hilt는 다른 유형의 종속 항목으로 또는 필드 삽입을 통해(ExampleActivity에서와 같이) AnalyticsAdapter를 제공할 때마다 AnalyticsAdapter의 새 인스턴스를 제공합니다.

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

아래 표에는 생성된 각 구성요소의 범위 주석이 나와 있습니다.

Android 클래스 생성된 구성요소 범위
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
@WithFragmentBindings 주석이 지정된 View ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

이 예에서 @ActivityScoped를 사용하여 AnalyticsAdapter의 범위를 ActivityComponent로 지정하면 Hilt는 해당 활동의 수명 주기 동안 동일한 AnalyticsAdapter 인스턴스를 제공합니다.

Kotlin

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

자바

@ActivityScoped
public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

AnalyticsServiceExampleActivity뿐만 아니라 앱의 모든 위치에서 매번 동일한 인스턴스를 사용해야 하는 내부 상태가 있다고 가정해 보겠습니다. 이럴 때에는 AnalyticsService의 범위를 SingletonComponent로 지정하는 것이 적절합니다. 결과적으로 구성요소는 AnalyticsService의 인스턴스를 제공해야 할 때마다 매번 동일한 인스턴스를 제공합니다.

다음 예에서는 Hilt 모듈에서 결합의 범위를 구성요소로 지정하는 방법을 보여줍니다. 결합의 범위는 결합이 설치된 구성요소의 범위와 일치해야 하므로 이 예에서는 ActivityComponent 대신 SingletonComponentAnalyticsService를 설치해야 합니다.

Kotlin

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

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

자바

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

// If you don't own AnalyticsService.
@Module
@InstallIn(SingletonComponent.class)
public class AnalyticsModule {

  @Singleton
  @Provides
  public static AnalyticsService provideAnalyticsService() {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

Hilt 구성요소 범위에 관한 자세한 내용은 Android 및 Hilt의 범위 지정을 참고하세요.

구성요소 계층 구조

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

ViewWithFragmentComponent는 FragmentComponent 아래에 있습니다. FragmentComponent 및 ViewComponent는 ActivityComponent 아래에 있습니다. ActivityComponent는 ActivityRetainedComponent 아래에 있습니다. ViewModelComponent는 ActivityRetainedComponent 아래에 있습니다. ActivityRetainedComponent 및 ServiceComponent는 SingletonComponent 아래에 있습니다.
그림 1. Hilt가 생성하는 구성요소의 계층 구조

구성요소 기본 결합

각 Hilt 구성요소는 Hilt가 고유한 맞춤 결합에 종속 항목으로 삽입할 수 있는 기본 결합 세트와 함께 제공됩니다. 이러한 결합은 일반 활동 및 프래그먼트 유형에 해당하며 특정 서브클래스에는 해당되지 않습니다. 이는 Hilt가 모든 활동을 삽입하는 데 단일 활동 구성요소 정의를 사용하기 때문입니다. 각 활동에는 이 구성요소의 다른 인스턴스가 있습니다.

Android 구성요소 기본 결합
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

애플리케이션 컨텍스트 결합은 @ApplicationContext를 통해 사용할 수도 있습니다. 예를 들면 다음과 같습니다.

Kotlin

class AnalyticsServiceImpl @Inject constructor(
  @ApplicationContext context: Context
) : AnalyticsService { ... }

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

자바

public class AnalyticsServiceImpl implements AnalyticsService {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ApplicationContext Context context) {
    this.context = context;
  }
}

// The Application binding is available without qualifiers.
public class AnalyticsServiceImpl implements AnalyticsService {

  private final Application application;

  @Inject
  AnalyticsAdapter(Application application) {
    this.application = application;
  }
}

활동 컨텍스트 결합은 @ActivityContext를 통해 사용할 수도 있습니다. 예를 들면 다음과 같습니다.

Kotlin

class AnalyticsAdapter @Inject constructor(
  @ActivityContext context: Context
) { ... }

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

Java

public class AnalyticsAdapter {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ActivityContext Context context) {
    this.context = context;
  }
}

// The Activity binding is available without qualifiers.
public class AnalyticsAdapter {

  private final FragmentActivity activity;

  @Inject
  AnalyticsAdapter(FragmentActivity activity) {
    this.activity = activity;
  }
}

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

Hilt에는 가장 일반적인 Android 클래스에 관한 지원이 함께 제공됩니다. 그러나 Hilt가 지원하지 않는 클래스에 필드 삽입을 실행해야 할 수도 있습니다.

이러한 경우 @EntryPoint 주석을 사용하여 진입점을 만들 수 있습니다. 진입점은 Hilt가 관리하는 코드와 그렇지 않은 코드 사이의 경계입니다. 즉, Hilt가 관리하는 객체의 그래프에 코드가 처음 들어가는 지점입니다. 진입점을 통해 Hilt는 Hilt가 관리하지 않는 코드를 사용하여 종속 항목 그래프 내에서 종속 항목을 제공할 수 있습니다.

예를 들어 Hilt는 콘텐츠 제공자를 직접 지원하지 않습니다. 콘텐츠 제공자가 Hilt를 사용하여 일부 종속 항목을 가져오도록 하려면 원하는 결합 유형마다 @EntryPoint로 주석이 지정된 인터페이스를 정의하고 한정자를 포함해야 합니다. 그리고 다음과 같이 @InstallIn을 추가하여 진입점을 설치할 구성요소를 지정합니다.

Kotlin

class ExampleContentProvider : ContentProvider() {

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

  ...
}

자바

public class ExampleContentProvider extends ContentProvider {

  @EntryPoint
  @InstallIn(SingletonComponent.class)
  interface ExampleContentProviderEntryPoint {
    public AnalyticsService analyticsService();
  }
  ...
}

진입점에 액세스하려면 EntryPointAccessors의 적절한 정적 메서드를 사용하세요. 매개변수는 구성요소 인스턴스이거나 구성요소 소유자 역할을 하는 @AndroidEntryPoint 객체여야 합니다. 매개변수로 전달하는 구성요소와 EntryPointAccessors 정적 메서드가 모두 @EntryPoint 인터페이스의 @InstallIn 주석에 있는 Android 클래스와 일치하는지 확인합니다.

Kotlin

class ExampleContentProvider: ContentProvider() {
    ...

  override fun query(...): Cursor {
    val appContext = context?.applicationContext ?: throw IllegalStateException()
    val hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java)

    val analyticsService = hiltEntryPoint.analyticsService()
    ...
  }
}

자바

public class ExampleContentProvider extends ContentProvider {

  @Override
  public Cursor query(...) {
    Context appContext = getContext().getApplicationContext();
    ExampleContentProviderEntryPoint hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class);
    AnalyticsService analyticsService = hiltEntryPoint.analyticsService();
  }
}

이 예에서는 진입점이 SingletonComponent에 설치되어 있으므로 ApplicationContext를 사용하여 진입점을 검색해야 합니다. 검색하려는 결합이 ActivityComponent에 있다면 ActivityContext를 대신 사용합니다.

Hilt 및 Dagger

Hilt는 Dagger 종속 항목 삽입 라이브러리를 기반으로 빌드되어 Dagger를 Android 애플리케이션에 통합하는 표준 방법을 제공합니다.

Dagger와 관련하여 Hilt의 목표는 다음과 같습니다.

  • Android 앱을 위한 Dagger 관련 인프라 간소화
  • 앱 간의 설정, 가독성 및 코드 공유를 용이하게 하기 위한 표준 구성요소 및 범위 세트 생성
  • 테스트, 디버그 또는 출시와 같은 다양한 빌드 유형에 서로 다른 결합을 프로비저닝하는 쉬운 방법 제공

Android 운영체제는 많은 자체 프레임워크 클래스를 인스턴스화하므로 Android 앱에서 Dagger를 사용하려면 상당한 양의 상용구를 작성해야 합니다. Hilt는 Android 애플리케이션에서 Dagger 사용과 관련된 상용구 코드를 줄입니다. Hilt는 자동으로 다음을 생성하고 제공합니다.

  • 달리 수동으로 생성해야 하는 Dagger와 Android 프레임워크 클래스를 통합하기 위한 구성요소
  • Hilt가 자동으로 생성하는 구성요소와 함께 사용할 범위 주석
  • Application 또는 Activity와 같은 Android 클래스를 나타내는 사전 정의된 결합
  • @ApplicationContext@ActivityContext를 나타내는 사전 정의된 한정자

Dagger 및 Hilt 코드는 동일한 코드베이스에 공존할 수 있습니다. 그러나 대부분의 경우 Android에서 Dagger의 모든 사용을 관리하려면 Hilt를 사용하는 것이 가장 좋습니다. Dagger를 사용하는 프로젝트를 Hilt로 이전하려면 이전 가이드Hilt로 Dagger 앱 이전 Codelab을 참조하세요.

추가 리소스

Hilt에 관해 자세히 알아보려면 다음 추가 리소스를 참조하세요.

샘플

Codelab

블로그