Android Dagger2에서 사용하기 편리하도록 출시된 Hilt에 대해서 간단히 정리했습니다. 기존의 Dagger2를 Android에 적용하려면 사전에 작업해야 되는 일이 많았다.
위의 할 일처럼 사전 작업이 많았는데 이번 Hilt에서는 방금 언급된 이 할 일들을 전부 자동으로 처리해주고 있었다. Hilt를 적용하기 위해서 대략적으로 아래의 단계가 있다.
@HiltAndroidApp
어노테이션 붙이기@Module
, @InstallIn({Component class})
어노테이션 붙이기@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
어노테션을 붙이지 않아도 주입 받을 수 있다.