[안드로이드] Hilt 개념 설명 및 사용법[2]

2 minute read

이전 글 에서 Hilt의 기본에 대해서 알아봤다.

간략히 정리를 해보면

  • Container :
    • 의존성(클래스) 관리
    • 각 의존성들이 생성되고 필요한 곳에 주입을 시켜준다.
  • @AndroidEntryPoint :
    • 안드로이드 생명주기에 맞는 의존성들의 컨테이너를 만든다.
  • @Inject : field-injection, constructor-injection 두가지가 있다.
    • 이 어노터에션이 붙은 변수(필드) 또는 생성자 파라미터에 의존성을 주입한다.
  • 모듈(@Module) : constructor-injection 할 수 없는 경우에 사용(추상 클래수,인터페이스, 외부 라이브러리)
    • @Binds : 모듈이 abstract 클래스인 메서드에서 사용
      • 파라미터 한개만 가능(abstract의 구현체)
      • function body 없음
    • @Provide : 모듈이 object 인 메서드에서 사용
      • 여러 파라미터 가능
      • function body 존재
    • @Binds나 @Provides에서 Hilt가 필요한 건 리턴 타입과 파라미터들 뿐이다.(이름은 그냥 적당히 지으면 된다. 보통 fun provide~ , fun bind~)
  • @InstallIn(~) : 모듈을 사용할 때 어느 안드로이드 컴포넌트에 맞춰 사용할 건지 지정

Qualifier(한정자)

  • 이 어노테이션(콸리퐈이어)이 필요한 때는 모듈 사용시 똑같은 리턴 타입이 있을 경우다.
  • @Qualifier 대신 @Named로 똑깥은 기능을 할 수도 있다.(방식은 조금 다름)



ex Car를 상속해 각각 기름 종류에 따라 자동차가 필요할 경우를 보면

enum class OilType{
    GASOLINE,DIESEL,LPG
}
interface Car{
    fun getOilType():OilType
    ...
}

@InstallIn(ActivityComponent::class)
@Module
abstract class CarModule{
    
    @Binds
    fun bindGasolineCar(impl:GasolineCar):Car
    @Binds
    fun bindDieselCar(impl:DieselCar):Car
    ...
}

만약 어디선가 Car을 요청할때 hilt는 두 바인드 메서드에서 어느 종류의 Car을 리턴해야 하는지 모른다.
이를 보완하기 위해서 @Qualifier가 등장한다.

@Qualifier
annotation class GasolineCar

@Qualifier
annotation class DieselCar

이렇게 어노테이션 클래스를 만들고

@GasolineCar
@Binds
fun bindGasolineCar(impl:GasolineCar):Car

@DieselCar
@Binds
fun bindDieselCar(impl:DieselCar):Car

이렇게 모듈에서 지정을 하고

@GasolineCar
@Inject lateinit var gasolineCar:Car

@DieselCar
@Inject lateinit var dieselCar:Car

이렇게 사용하면 된다.

그리고 @Provides를 사용할때도 똑같다. 리턴 타입은 동일하나 파라미터가 다른 경우가 있겠다.

ex

@InstallIn(ApplicationComponent::class)
@Module
abstract class 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()
  }
}

Predefined qualifiers(미리 정의된 한정자)

  • @ActivityContext
  • @ApplicationContext

이 두가지를 hilt에서 제공해준다. 정말 편하다.



@EntryPoint(진입점)

  • @AndroidEntryPoint를 사용하지 못하는 경우 사용됨
  • 이 어노테이션은 hilt에서 지원하지 않는 의존성 주입에 사용된다.
  • Hilt는 안드로이드 생명주기에 특화된(보일러플레이트코드를 줄여주는) DI라고 생각해도 되겠다.
    그러니 안드로이드 생명주기와 상관없는 단순 feature module같은 경우는 hilt가 아닌 dagger을 이용해야한다.
    (일반 클래스에서 hilt가 @Inject를 찾질 못한다.이런 경우 @EntryPoint가 필요하다.)

이 Car함수에서 local db를 사용한다고 가정하면 요론 식으로 사용할 수 있겠다.

class GasolineCar(private val context:Context):Car{
    override fun oilType() : OilType = OilType.GASOLINE

    //필요한 스코프를 생각해서 InstallIn을 정한다.
    @InstallIn(ApplicationComponent::class)
    @EntryPoint // 이 녀석으로 hilt가 entryPoint임을 알 수 있게 된다.
    interface DbEntryPoint{
        fun database():AppDatabase
    }

    private fun getDb(context: Context):AppDatabase{
        //EntryPointAccessors:static accessor
        //다음의 파라미터들은 hilt-component가 알 수 있는 값들이어야 한다.
        //context는 디폴트로 가지고 있고, DbEntryPoint는 위에서 지정해 주었다.
        return EntryPointAccessors.fromApplication(
            context,
            DbEntryPoint::class.java
            ).database()
    }

    //그리고 사용한다.                                    
    fun getDestination() = getDb(context).mapDao.destination 
}

EntryPointAccessors의 메서드

  • fromApplication(context,entryPoint:Class<T!>)
  • fromActivity(context,entryPoint:Class<T!>)
  • fromFragment(context,entryPoint:Class<T!>)
  • fromView(context,entryPoint:Class<T!>)

이렇게 나와있다. 해당 @InstallIn(인터페이스 위의)에 맞춰주면 된다.




Hilt를 폭 넓게 사용하려면 dagger도 알아야 한다.

dagger의 component, subComponent, Builder, Factory 등등에 대한 좋은 정보는

여기서 공부하면 좋다. 영어긴 한데 독일분인지 발음이 딱딱 떨어져 좀 듣기 수월하기도하고 코드만 대충 봐도 이해가 될 듯 싶다. » 유튜브 강의




더 자세한 공부는 여기서
android-doc-link
android-hilt-codelab-link

Leave a comment