Beeeam

Hilt? 본문

Android

Hilt?

Beamjun 2023. 4. 11. 23:10

Hilt

Hilt는 안드로이드에서 의존성 주입을 도와주는 라이브러리이다. Hilt는 프로젝트의 모든 클래스에 컨테이너를 제공하고, 수명 주기를 자동으로 관리해준다.

Hilt는 기존에 사용되던 Dagger 라이브러리를 기반으로 만들어졌다. Dagger보다 쉬워서 러닝 커브가 낮고, 초기 DI 환경 구축 비용을 절감할 수 있는 장점이 있다.

 

이전 게시물에서 언급했던 내용이다. 이번에는 더 자세히 알아보자

 

Hilt 사용

Project level의 gradle에 밑의 내용 추가

buildscript {
		...
    dependencies {
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
    }
}

App-level의 gradle에 밑의 내용 추가

plugins {
//    Hilt
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}
...

dependencies {
    // Hilt dependencies
    def hilt_version = "2.44"
    implementation "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}

각 gradle 파일에 위의 내용을 추가하고 sync를 해야지 Hilt를 프로젝트에서 사용할 수 있다.

 

애플리케이션에서 Hilt 사용

Hilt를 사용하는 모든 애플리케이션에서는 Application class에 @HiltAndroidApp 주석을 추가 해야 한다.

컴파일 과정에서 DI에 필요한 요소들을 초기화 하기 위해서 사용한다.

(기본적으로 생성되지 않으니 class 추가를 하자)

@HiltAndroidApp
class LogApplication : Application()

 

AndroidEntryPoint

Hilt는 @AndroidEntryPoint 주석이 있는 다른 클래스에 객체를 주입할 수 있다. 이 어노테이션은 의존성 주입 시작점을 나타낸다.

Hilt는 밑의 클래스들을 지원한다.

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

클래스에 @AndroidEntryPoint 주석을 지정하면 해당 클래스를 상속 받는 클래스들도 @AndroidEntryPoint 주석을 지정 해야한다.

@Inject 주석을 사용하여 의존성을 주입 받으려는 변수에 객체를 주입할 수 있다. 이를 필드 삽입이라고 한다.

(필드 삽입은 private 타입일 수 없다.)

@AndroidEntryPoint
class LogsFragment : Fragment() {

	// 필드 삽입
    @Inject lateinit var logger: LoggerLocalDataSource
    @Inject lateinit var dateFormatter: DateFormatter
		...
}

 

인스턴스 제공 방법

어떤 클래스에서 어떤 객체가 필요하다고 한다. Hilt가 이 객체를 주입 해주려면 이 객체를 어떻게 생성하는지 알아야 한다. 이렇게 객체를 생성하는 방법 (인스턴스 제공 방법)에는 2가지가 있다.

  • @Inject
  • Hilt 모듈(@Module, @Provides, @Binds)

@Inject

생성자 삽입 방법이다. 클래스의 생성자에 @Inject 주석을 달아서 클래스의 인스턴스 제공 방법을 Hilt에게 알려준다.

class LoggerLocalDataSource @Inject constructor(private val logDao: LogDao) {
		...
}

 

Hilt 모듈

외부 라이브러리를 사용하는 경우(retrofit, room, … ) 또는 인터페이스 처럼 생성자가 없어서 생성자 삽입 방법을 사용할 수 없을 때 Hilt 모듈을 사용하여 의존성을 생성한다.

@Module

@Module 주석으로 지정된 클래스로 @InstallIn 주석을 지정하여 각 모듈을 사용하거나 설치할 클래스를 Hilt에 알린다.

@InstallIn(SingletonComponent::class)
@Module
object DatabaseModule {
    ...
}

@Provides

@Provides 주석을 사용하여 인스턴스를 삽입할 수도 있다.

Kotlin에서는 @Provides 만 포함하는 모듈은 object 클래스가 될 수 있다.

@Provides 주석을 가진 함수는 Hilt에서 이 유형의 인스턴스를 제공할 때 마다 실행된다. 그리고 이러한 함수의 반환 타입은 Hilt에 어떤 유형의 인스턴스를 제공하는지 알려준다.

@InstallIn(SingletonComponent::class)
@Module
object DatabaseModule {
    @Provides
    fun provideLogDao(database: AppDatabase): LogDao {
        return database.logDao()
    }
}

이런 형식으로 구현된다.

 

@Binds

인터페이스의 인스턴스를 제공해야 할 때 사용할 구현을 Hilt에 알려준다. 인터페이스에 사용할 구현을 Hilt에 알리려면 Hilt 모듈 내 함수에 @Binds 주석을 사용하면 된다.

 

인스턴스 범위 지정

주석을 사용하여 인스턴스의 범위를 컨테이너로 지정할 수 있다. Hilt는 다양한 수명 주기를 가진 컨테이너를 생성할 수 있다.

ex) Application 컨테이너 범위 → @Singleton (항상 같은 인스턴스 제공)
      Activity 컨테이너 범위 → @ActivityScoped (Activity 동안 같은 인스턴스 제공)

@Singleton
class LoggerLocalDataSource @Inject constructor(private val logDao: LogDao) {
    ...
}

 

한정자

Hilt에 동일한 유형의 다른 구현(여러 개의 결합)을 제공하는 방법을 알릴 때 사용한다. 결합을 식별하는 데 사용되는 주석이다.

@Qualifier
annotation class InMemoryLogger

@Qualifier
annotation class DatabaseLogger

@InstallIn(SingletonComponent::class)
@Module
abstract class LoggingDatabaseModule {

    @DatabaseLogger
    @Singleton
    @Binds
    abstract fun bindDatabaseLogger(impl: LoggerLocalDataSource): LoggerDataSource
}

@InstallIn(ActivityComponent::class)
@Module
abstract class LoggingInMemoryModule {

    @InMemoryLogger
    @ActivityScoped
    @Binds
    abstract fun bindInMemoryModule(impl: LoggerInMemoryDataSource): LoggerDataSource
}

위의 코드처럼 구현이 된다. @Qualifier 주석을 사용하여 각 한정자의 이름을 선언한다. 그리고 각각의 @Binds 또는 @Provides 함수에 해당하는 한정자 이름 주석을 달아서 구분을 한다.

한정자는 삽입하려는 구현과 함께 삽입 지점에서 사용해야 한다.

 

@EntryPoint

Hilt에서 지원하지 않는 클래스에 종속 항목 삽입할 때는 @EntryPoint 주석을 사용한다. EntryPoint는 종속 항목을 삽입하는데 Hilt를 사용할 수 없는 코드에서 Hilt가 제공하는 객체를 가져올 수 있는 경계 지점이다.

Ex) ContentProvider