Android/이론 학습

[Android] Hilt 의 다양한 어노테이션을 알아보자

노소래 2022. 2. 19. 00:57

[DI 가 무엇인지 모른다면?]

아래 링크를 먼저 참고하자

 

[Android] 안드로이드의 DI (Dependency Injection)

[DI 개념과 필요한 이유] 한 클래스는 다른 클래스를 참조해야하는 일이 빈번하다. (특히 디자인패턴을 다루면서) 예를 들어 Computer 클래스를 실행시키려면 Computer 클래스 내에서 Cpu 클래스 객체

nosorae.tistory.com

[Hilt 란?]

힐트는 제트팩에서 권장하는 DI 라이브러리이다.
힐트는 Dagger 위에 구축되어있고 대거 컴포넌트를 만든다.  (compile-time correctness, runtime performance, scalability 측면에서 이득)
따라서 힐트 역시 compile-time dependencies, runtime performance, scalability 라는 이점이 있다.
프로젝트에 있는 모든 안드로이드 클래스에 컨테이너를 제공하고 그들의 라이프사이클을 자동으로 관리해준다.

또한 의존성 그래프를 만들고 잘못된 의존성이나 의존성 사이클이 없는지 컴파일타임에 잡아준다.

런타임중에 실제 객체들과 그들의 의존성을 만들기위해 클래스를 생성해준다.

[Hilt 사용법]

  • gradle 설정 
    • classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
    • id 'kotlin-kapt' // for hilt, ~
      id 'dagger.hilt.android.plugin' // for hilt
    • implementation "com.google.dagger:hilt-android:2.38.1"
      kapt "com.google.dagger:hilt-compiler:2.38.1"
  • 어노테이션 정리
    • @HiltAndroidApp
      Application 확장 클래스에 붙여서
      힐트의 코드를 생성할 수 있음
    • @AndroidEntryPoint
      힐트 컴포넌트를 생성
      (ComponentActivity, androidx.Fragment, View, Service, BroadcastReceiver)
      Abstract class 는 이 어노테이션이 필요없다.
    • @HiltViewModel
    • @Inject
      필드에 주입
      다른 안드로이드 클래스에 의존성을 제공받을 수 있다.
      힐트에 의해 주입된 필드멤버는 private 일 수 없음 (private 이면 컴파일 에러, @Inject constructor() 에선 가능)
    • @Inject constructor()
      생성자에 주입
      의존성으로서 인스턴스에게 타입을 제공하는데 필요한 binding information 
      생성자에 힐트가 그 클래스에 어떻게 인스턴스를 제공할지 알려준다.
    • @Module
      주입할 객체 타입이 내가 가진 게 아닐 때, construct-inject 가 불가할 때 힐트 모듈을 사용하여 바인딩 정보를 제공
      construct-inject 가 불가할 때란 Interface, external library(ex Retrofit, OkHttpClient, Room databases, instance created by builder pattern)
    • @InstallIn(~::class)
      어느 안드로이드 클래스에 힐트가 사용될지 알려주기 위해 힐트모듈에 사용
      괄호 안에 ActivityComponect::class 라고 표시한다면 모든 앱의 액티비티가 모듈안의 객체에 의존할 수 있다는 의미이다.
      Hilt component Injector for
      SingletonComponent Application
      object 클래스 (?)
      Hilt doesn't generate a component for broadcast receivers because Hilt injects broadcast receiversdirectly from
      SingletonComponent.
      ViewModelComponent ViewModel
      ActivityComponent Activity
      FragmentComponent Fragment
      ViewComponent View
      ViewWithFragmentComponent View annotated with @WithFragmentBindings
      ServiceComponent Service
      ActivityRetainedComponent N/A
    • @Binds
      인터페이스는 constructor-inject 할 수 없으므로 힐트 모듈안에 abstract function 에 어노테이트
      인터페이스의 어느 구현체가 객체로 사용될지 알려준다.
      함께 쓰인 abstract function 의 리턴타입은 인터페이스를 말하고 파라미터는 어느 구현클래스인지 말해준다.
    • @Provides
      external library(ex Retrofit, OkHttpClient, Room databases, instance created by builder pattern) 같은 경우도 construct-inject 할 수 없다고 했는데
      이 때는 힐트 모듈 안에 Provides 어노테이션을 쓰는 것 같다 (왜 Binds 와 통일하지 않았을까?)
      함수의 리턴타입은 어떤 타입의 인스턴스를 제공하는지 알려주고 파라미터는 어떤 타입의 객체를 주입받을지 알려준다. 여기서 파라미터로 주입 받는 인스턴스는 당연히 다른 곳에서 제공하고 있어야한다. 
      함수의 바디부분은 힐트가 그 해당 리턴타입을 어떻게 제공할지 알려준다.
    • @ApplicationContext
      predefined qualifiers 중 하나이다.
    • @ActivityContext
      predefined qualifiers 중 하나이다.

 

예시 코드)

@Module
@InstallIn(SingletonComponent::class) // 모듈안에 있는 의존성의 스코프
object AppModule  {

    @Provides
    @Singleton // 스코프동안 싱글톤 객체로 지정
    fun provideParprikaApi(): CoinPaprikaApi {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create()) // 시리얼 디시리얼라이즈 데이터
            .build()
            .create(CoinPaprikaApi::class.java)
    }

    @Provides
    @Singleton
    fun provideCoinRepository(api: CoinPaprikaApi): CoinRepository { // 위에서 힐트가 CoinPaprikaApi 어떻게 만들지 알려줬기 때문에 인자로 넣어준다.
       return CoinRepositoryImpl(api)
    }

}

//코드 출처 : https://youtu.be/EF33KmyprEQ