1. Hilt에서 ViewModel 인스턴스 얻기

Hilt는 ViewModel의 의존성 주입을 효과적으로 관리하기 위해 @HiltViewModel 어노테이션과 ViewModelComponent를 제공한다.

@HiltViewModel 어노테이션 처리

Hilt에서 by viewModels() 델리게이트로 처리된 ViewModel 인스턴스들의 멀티바인딩은 다음과 같이 이루어진다.

@HiltViewModel 어노테이션 사용

Hilt는 @HiltViewModel 어노테이션이 붙은 ViewModel 클래스들을 찾아 자동으로 멀티바인딩 모듈을 생성한다.

@HiltViewModel
class MainViewModel @Inject constructor(...) : ViewModel()

Hilt가 생성한 멀티바인딩 모듈

Hilt는 @HiltViewModel 어노테이션이 붙은 ViewModel 클래스에 대해 자동으로 다음과 같은 모듈을 생성한다.

@Module
@InstallIn(ViewModelComponent.class)
public abstract static class BindsModule {
    @Binds
    @IntoMap
    @StringKey("해당viewmodel패키지위치.MainViewModel")
    @HiltViewModelMap
    public abstract ViewModel binds(MainViewModel vm);
}

여기서 주목할 점은 @StringKey를 사용하여 키 값으로 ViewModel의 전체 클래스 이름을 문자열로 사용한다. 그리고 @HiltViewModelMap 어노테이션을 사용하여 ViewModel 맵을 관리한다. 자세하게 말하자면 ViewModel 인스턴스를 제공하는 Map에 대한 멀티바인딩을 위해 사용하여 각 ViewModel 클래스에 대해 문자열 키(클래스의 전체 이름)와 해당 ViewModel의 Provider를 매핑시킨다.

ViewModel 키 제공

Hilt는 또한 각 ViewModel에 대한 키를 제공하는 모듈을 생성한다.

@Module
@InstallIn(ActivityRetainedComponent.class)
public static final class KeyModule {
    @Provides
    @IntoSet
    @HiltViewModelMap.KeySet
    public static String provide() {
        return "해당ViewModel패키지.MainViewModel";
    }
}

여기서 ViewModel 키는 왜 ActivityRetainComponent로 설정 되어 있다. ActivityRetainedComponent가 configuration changes에도 생존하는 컴포넌트이고 ViewModel도 마찬가지로 configuration changes를 거쳐 생존해야 하므로, ViewModel의 키를 ActivityRetainedComponent에 제공하는 것이 적절하기 때문이다.

@HiltViewModelMap.KeySet은 Hilt 내부에서 사용되는 한정자(qualifier)로 ViewModel 클래스 이름들의 Set을 멀티바인딩하는 데 사용된다. 이를 통해 Hilt는 어떤 ViewModel들이 사용 가능한지 추적할 수 있다.

ViewModel Factory

Hilt는 이러한 바인딩을 사용하여 내부적으로 HiltViewModelFactory를 구현합니다. 이 팩토리는 멀티바인딩된 Map을 사용하여 필요한 ViewModel 인스턴스를 생성한다.