티스토리 뷰

개발/안드로이드

hilt 힐트

희누 2021. 10. 9. 19:30

https://developer.android.com/training/dependency-injection/hilt-android

힐트는 대거를 사용하기 편하게 감싼 di 라이브러리

힐트를 쓰려면 HiltAndroidApp어노테이션을 application에 붙여함

  • 힐트코드제너레이션을 트리거 시킴
  • 어플리케이션 레벨의 의존성 컨테이너 역할을 하는 어플리케이션 베이스 클래스를 만들어줌

의존성주입이 어려운 경우에는 hiltModule을 사용하면 된다.

HiltModule에는 InstallIn도 적어줘여야하는데 instalIn인의 경우 의존성이 주입될 범위를 나타낸다.

만약에 SingletonComponent면 전역에 걸쳐 힐트모듈에 적인 의존성 타입을 주입할수 있다는 것이고

ActivityComponent면 액티비티에서 사용할수있다는 것이다. 즉 주입할 곳을 제한한다고 생각하면된다.

여기서 힐트에서 사용되는 액티비티는 ComponentActivity를 상속받은 액티비티를 말한다.

여기서 ActivityRetainedComponent랑 ActivityComponent에 주목하면 재밌는데

둘다 주입할 곳이 액티비티이긴한데 ActivityRetainedComponent 구성이 변경되었을때(예를 들어 언어,화면 크기등등)를 대비하는 컴포넌트로서 액티비티의 첫 create때 생성되고 마지막 onDestroy때 디스트로이된다.

그런데 갑자기 등장한 component가 무엇이냐하면 주입할 클래스가 힐트 컴포넌트로 클래스로 제너레이션 된다.

FirstReservationActivity →Hilt_FirstReservationActivity

이걸 액티비티컴포넌트라고 하는 것.

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

    @Binds
    abstract fun provideYoonah(yoonahImpl: YoonahInterfaceImpl): YoonahInterface
}

@ActivityScoped
class YoonahInterfaceImpl @Inject constructor():YoonahInterface {
    init {
        LazyLogger.debug("tag1 yoonah interface init!")

    }
    override fun printYoonah() {
        LazyLogger.debug("tag1 yoonah interface imple print")
    }
}

//in Activity
@Inject
    lateinit var yoonah: YoonahInterface
  • viewModel의 생성자에 YoonahInterface를 주입하고 빌드하니까 에러남
    • ActivityScope이 잘 돌아감
  • AActivity → BActivity
    • 둘 다 YoonahInterface를 @Inject함
    • A,B 각각 onCreate되면서 init이 호출됨
    • (아...메모리 해제가 잘되는지 보고싶었는데..)

@Singleton may not reference bindings with different scopes:
public abstract static class SingletonC implements

예제1)

@Module
@InstallIn(SingletoneComponent::class)
abstract class YoonahModule {

    @Binds
    abstract fun provideYoonah(yoonahImpl: YoonahInterfaceImpl): YoonahInterface
}

@ActivityScoped
class YoonahInterfaceImpl @Inject constructor():YoonahInterface {
    init {
        LazyLogger.debug("tag1 yoonah interface init!")

    }
    override fun printYoonah() {
        LazyLogger.debug("tag1 yoonah interface imple print")
    }
}

//in Activity
@Inject
    lateinit var yoonah: YoonahInterface

//gerated code 
@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class YoonahInterfaceImpl_Factory implements Factory<YoonahInterfaceImpl> {
  @Override
  public YoonahInterfaceImpl get() {
    return newInstance();
  }

  public static YoonahInterfaceImpl_Factory create() {
    return InstanceHolder.INSTANCE;
  }

  public static YoonahInterfaceImpl newInstance() {
    return new YoonahInterfaceImpl();
  }

  private static final class InstanceHolder {
    private static final YoonahInterfaceImpl_Factory INSTANCE = new YoonahInterfaceImpl_Factory();
  }
}
  • 터짐
    • scoped with @Singleton may not reference bindings with different scopes:
      public abstract static class SingletonC implements
  • 싱글톤 컴포넌트에 액티비티스콥은 안됨
  • ActivityScoped 지우면 잘 돌아감
    • A,B 클래스둘다 inject하면 2개의 yoonah가 생김... 인스턴스가 몇번 생길지에 대한건 scope에서 처리하는거라는걸 잘 알 수 있었고
    • 스콥을 최대한 적게 잡으라고 하던데 그 이유도 알 수 있었음
    • Note: Scoping a binding to a component can be costly because the provided object stays in memory until that component is destroyed. Minimize the use of scoped bindings in your application. It is appropriate to use component-scoped bindings for bindings with an internal state that requires that same instance to be used within a certain scope, for bindings that need synchronization, or for bindings that you have measured to be expensive to create.

예제2) @singleton의 위치에 따라 에러가 나고 안남

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

    @Binds
        @Singleton
    abstract fun provideYoonah(yoonahImpl: YoonahInterfaceImpl): YoonahInterface
}
  • 이렇게 하면 오류남
  • 근데 singleton을 yoonahInterfaceImpl에 붙이면 잘 돌아가고 싱글턴으로 돌아감
  • 또한 싱글톤이라고 미리 만들지 않고 호출할때 lazy하게 객체생성

예제3)

@ActivityScoped
class Yoonah @Inject constructor(){
    init {
        LazyLogger.debug("tag1 init!")
    }
}
  • InstallIn()을 코드에 넣으니까 에러남
    • @InstallIn-annotated classes must also be annotated with @Module or @EntryPoint
    • 일반 클래스의 경우에는 component를 적지 않는다.
  • @Inject constructor() 자꾸 까먹는다...

 

실제로 쓰는 경우를 생각해보면
인터페이스를 주입해야하는 경우는 hiltModule을 쓰게 되고 이때는 Component랑 Scope를 생각해야함
만약 내가 만든 클래스를 주입하는 경우는 그냥 해당 클래스의 Scope만 생각하면된다.
둘다 실제로 inject할때 생성자에 넣기 싫으면 @Inject 사용하여 public lateinit var를 선언하면된다.

 

 

'개발 > 안드로이드' 카테고리의 다른 글

특정화면으로 이동하는 DynamicLink 만들기  (0) 2022.08.05
DataBinding 의 default는 거짓이다.  (0) 2022.04.19
StateFlow  (0) 2021.03.03