https://dagger.dev/hilt/design-overview


10.1 Design Decisions – Design Overview

컴포넌트 생성과 모듈/Entry point 설치

Hilt는 전이 클래스 경로에서 모든 모듈과 Entry point를 찾아 컴포넌트를 생성한다. 모든 모듈의 @InstallIn 어노테이션과 Entry point는 정의된 패키지에서 작은 메타데이터 클래스를 생성하게 된다. @HiltAndroidApp를 처리 할 때 컴포넌트에 설치해야 하는 모든 집계된 항목을 찾기 위해 특수 패키지를 검사한다. @DefineComponent 및 @AliasOf와 같은 다른 헬퍼 클래스에도 동일한 전략이 사용된다.

동시에 안드로이드 Application이 생성되기 때문에, 생성된 Application은 최상위 컴포넌트인 ApplicationComponent를 직접 참조 할 수 있게 된다.

HiltTestApplication은 반드시 여러가지 테스트들을 지원해야하기 때문에, 프로덕션 애플리케이션에서와는 달리, 생성된 컴포넌트를 찾기 위해 리플렉션이 사용된다. 이는 테스트 Application을 테스트와 빌드에서 분리 할 수 있기 때문에 각 프로젝트에서 테스트 Application을 코드로 작성하는 대신 Hilt가 편리한 기본값을 제공 할 수 있기 때문에 유용하다. 리플렉션은 가치가 낮고 더 많은 비용이 들어가기 때문에 프로덕션에서는 사용되지 않는다.

클래스 경로에서 모든 모듈을 통합하는 것은 테스트에서 잘 동작한다. 왜냐하면 테스트는 테스트 클래스에서 내재된 클래스에 의한 바인딩을 쉽게 추가 할 수 있기 때문이다 (또는 모듈을 생성하는 @BindValue를 사용하는 것 보다 낫다). 마찬가지로 모듈 감지를 통해 클래스가 @Module 클래스를 내부 클래스로 포함 할 수도 있다. 이를 통해 Dagger 바인딩 없이 클래스를 사용할 수 없고 오류 발생 가능성이 줄어 들게 된다(예. 클래스를 @BindsOptionalOf와 연결하거나 소비하는 @Binds와 인터페이스).

@AndroidEntryPoint injection

@AndroidEntryPoint는 Gradle 플러그인의 변환을 통해 사용자 코드가 직접 또는 간접적으로 확장되는 기본 클래스를 생성하여 작동한다. 이 기본 클래스는 (상위 Hilt 인터페이스를 통해) 부모 컴포넌트를 검색하고, 컴포넌트를 만들고, 클래스를 주입하고, Hilt 인터페이스를 통해 하위에 컴포넌트를 노출시키는 역할을 한다.

예를들면, Activity에 주입하기 위해 생성된 코드는 다음과 같은 작업을 수행한다.(가독성을 위해 간소화함)

@Override public void onCreate(Bundled savedInstanceState) {
    // 이는 Application에서 상위 컴포넌트를 가져온다.
    // (실제로 부모로써 컴포넌트를 유지하는 Activity가 있다)
    Object parentComponent =((GeneratedComponentManager) getApplication()).generatedComponent();
    // 이것은 Activity 컴포넌트를 생성한다.이는 상위 컴포넌트가 Activity 컴포넌트를 빌드하는 메소드를
    // 가지고 있다는 것을 알기 위해 안전하지 않은 캐스팅을 포함한다.
    Object activityComponent = ((ActivityComponentBuilderEntryPoint) parentComponent)
        .activityComponentBuilder()
        .activity(this)
        .build();
    // 이는 Activity에 주입하고, 또한 Activity주입 메서드에 접근하기 위해 
    // 안전하지 않는 캐스팅을 포함하는 것을 의미 한다. 다른 안전하지 않은 캐스팅과 같이
    // 이러한 캐스팅은 빌드 의존성들을 깨고, 코드생성과 
    //  모듈/인터페이스의 클래스 경로 발견을 통해 보장되기 때문에 안전하다
    (MyActivity_GeneratedInjector) activityComponent).inject(this);
}

이러한 모든 글루 코드(glue code)의 생성으로 안전하지 않은 캐스팅과 함께 의존성을 깨뜨리고 안전하고 쉽게 만들 수 있다. 또한 생성된 인터페이스를 사용하는 Activity와 결합된 자동 검색은 @AndroidEntryPoint를 포함하거나 제거하면 @AndroidEntryPoint에 대한 종속성이 모두 추가/제거된다.

대부분의 경우 상위 컴포넌트를 쉽게 얻을 수 있지만, View와 Fragment의 경우 View가 Activity의 Context를 가져 오기 때문에 쉽지 않다. Fragment 바인딩이 있는 View를 지원하기 위해 Fragment에 대해 생성된 기본 클래스는 getLayoutInflater를 재정의하여 View에 대한 Dagger 컴포넌트를 보유하는 ContextWrapper에서 Context를 감싼다.

Hilt에서 이런 모든 설계 결정은 표준화됨으로써, Activity/Fragment/View와 함께 라이브러리를 구현하는 것은 더욱 쉬워 진다.

카테고리: Dagger2

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.