" Park 기술 블로그 "
# 카테고리
# 도구
[Android 안드로이드] Hilt에 대해서
2021-12-23 10:01:16

Android Dagger2에서 사용하기 편리하도록 출시된 Hilt에 대해서 간단히 정리했습니다. 기존의 Dagger2를 Android에 적용하려면 사전에 작업해야 되는 일이 많았다.

  • Component 만들기 및 Builder 혹은 Factory 생성
  • Component를 생성 후 Application class에 변수로 인스턴스 생성
  • 각 Activity나 Fragment 등에서 의존성 주입 요청
  • AAC ViewModel을 사용할 경우 Dagger2로부터 의존성 주입을 받고 View의 lifecycle에 종속적으로 동작하기 위해 ViewModelProvider.Factory를 통한 초기화
  • 등등..

위의 할 일처럼 사전 작업이 많았는데 이번 Hilt에서는 방금 언급된 이 할 일들을 전부 자동으로 처리해주고 있었다. Hilt를 적용하기 위해서 대략적으로 아래의 단계가 있다.

  1. Application class에 @HiltAndroidApp 어노테이션 붙이기
  2. 의존성 주입을 위한 @Module, @InstallIn({Component class}) 어노테이션 붙이기
  3. 의존성 주입 받을 Activity나 Fragment에 @AndroidEntryPoint 어노테이션 붙이기
  4. AAC ViewModel을 사용한다고 하면 ViewModel에 생성자에 @Inject 를 붙이고, Class에 @HiltViewModel 붙이기

 

위의 각 순서들 뒤의 ‘어노테이션 붙이기’ 라는 단어만 봐도 알겠지만 Hilt 적용을 위해서는 어노테이션만 붙이는 게 대부분이다. 조금 자세하게 소스를 보면서 정리할 예정이다. 설명을 위한 예제 프로젝트는 [GithubSearcher]에서 확인할 수 있다.

 

1. Application class에 @HiltAndroidApp 어노테이션 붙이기

위의 사진처럼 Application class에 @HiltAndroidApp 어노테이션만 붙이면 된다.

 

2. 의존성 주입을 위한 @Module, @InstallIn({Component class}) 어노테이션 붙이기

위의 사진처럼 @Module@InstallIn({Component class}) 어노테이션을 붙이면 된다. @InstallIn 안에 있는 {Component class}는 Hilt에서 기본으로 제공하는 Component이나 직접 정의한 Component를 넣어주면 된다. Hilt에서 기본으로 제공하는 Component는 아래와 같다.

위의 ApplicationComponent는 최신버전에서 SingletonComponent로 대체되었다. 그리고 각 Component에 대해서 기본으로 제공하는 Sope도 있다.

 

3. 의존성 주입 받을 Activity나 Fragment에 @AndroidEntryPoint 어노테이션 붙이기

위의 사진처럼 의존성 주입 받을 Activity에 @AndroidEntryPoint 어노테이션을 붙여준다. 그리고 사진에서는 ViewModel 외에 의존성 주입 받을 내용이 없어서 안 나와있지만 의존성 주입을 요청할 property에 아래와 같이 @Inject 어노테이션을 붙여준다.

@AndroidEntryPoint
class SampleActivity : AppCompatActivity() {
  
  @Inject
  lateinit var reposioty: UserRepository
  
  ...
  
}

 

4. AAC ViewModel을 사용한다고 하면 ViewModel에 생성자에 @Inject 를 붙이고, Class에 @HiltViewModel 붙이기

위의 사진처럼 생성자 앞에 @Inject를 붙이고 @HiltViewModel를 붙여준다. 그러면 이 ViewModel을 요청하는 @AndroidEntryPoint 어노테이션이 붙은 Activity의 viewModels 확장 함수를 통해서 초기화된다.

 

이러한 ViewModel 전달이 가능한 이유는 @AndroidEntryPoint 어노테이션의 원리와 viewModels 확장 함수의 합작이다. @AndroidEntryPoint 어노테이션이 붙은 Activity를 통해서 생성되는 Java Code를 확인해 보면 해당 Activity가 상속받을 base class를 생성하고 있다. 아래의 사진과 같이 Hilt의 Gradle Plugin에서 바이트 코드 변환 후 base class로 변경한다.

그리고 이 base class에서 getDefaultViewModelProviderFactory() 함수를 override해서 AbstractSavedStateViewModelFactory를 리턴하도록 구현되어 있다.

이 Factory를 viewModels 확장 함수에 어떻게 전달하는지에 대해서는 viewModels가 Activity 혹은 Fragment에 대한 확장 함수라는 점에 있다. 이 viewModels의 내부로직은 다른 Factory 인스턴스를 전달해주지 않으면 viewModels가 속한 Activity 혹은 Fragment의 getDefaultViewModelProviderFactory() 함수를 통해 기본 Factory를 전달 받도록 되어있다. 위에서 얘기한 것처럼 Hilt에서 이 함수를 override해서 AbstractSavedStateViewModelFactory를 전달하도록 했으니 자연스럽게 viewModels에 이 Factory가 흘러 들어가도록 된 것이다. ( 사람들 참 똑똑하다.. )

 

getDefaultViewModelProviderFactory() 함수를 override한 부분은 아래의 사진에서 볼 수 있고

이 함수를 호출하는 viewModels 확장 함수 로직은 아래와 같다.

그리고 Hilt에서 제공하는 Factory는 아래와 같다.

위의 사진의 hiltViewModelFactory가 AbstractSavedStateViewModelFactory이다. 그리고 108~112번째 줄을 보면 알 수 있 듯이 해당 Factory에 Multi-Binding으로 ViewModel이 없으면 Activity가 원래 갖고 있던 defaultFactory에서 create하는 것을 볼 수 있다. 저 delegateFactory의 출처를 따라가보면 원래 갖고 있던 defaultFactory인 걸 볼 수 있다.

 

그리고 ViewModel을 hiltViewModelFactory로 전달되는 부분은 일반 Dagger2에서 Multi-Binding을 통해 ViewModel을 초기화하는 로직과 동일하다. 먼저 ViewModel에 @HiltViewModel 어노테이션이 붙으면 Hilt가 이 ViewModel을 생성하는 Factory, 그리고 @Binds를 통해 ViewModel class 형태로 업캐스팅하는 Module, 그리고 이 ViewModel을 분류하는 KeySet 생성 Factory 이렇게 3가지를 만든다. 이 3가지의 클래스를 통해 @HiltViewModelMap@StringKey를 사용해서 Multi-Binding 하도록 구성되어 있다.

 

아래는 특정 ViewModel을 @Binds를 통해 ViewModel class 형태로 업캐스팅하는 것과 동시에 Multi-Binding하는 부분이고

아래는 이 ViewModel을 Multi-Binding으로 전달받는 소스이다.

그리고 이 getHiltViewModelMap 함수를 hiltViewModelFactory 소스 코드 사진의 91번째 줄에서 호출하고 있고 이 Map에서 ViewModel을 가져오는 방식으로 구현되어 있다. 위의 내용과 같이 AAC ViewModel에 대한 의존성 주입은 상속과 확장 함수의 개념을 통해 주입되기 때문에 @Inject 어노테션을 붙이지 않아도 주입 받을 수 있다.

0
# 댓글 + 새 댓글 작성
# 새 댓글 작성
댓글 암호는 댓글 삭제 시 필요합니다.
# 님에게 답변 작성
대댓글 암호는 댓글 삭제 시 필요합니다.
# 님의 댓글 삭제
댓글 작성 시 입력했던 암호를 입력해주세요.
아직 댓글이 없습니다