마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Hilt 쪼금 질문좀 드립니다

0 추천

Hilt 공부중인데.. 몇가지 좀 헷갈리는게 있어서 질문드립니다.

 

1. @HiltAndroidApp로 Hilt 코드를 트리거 하면 앱 레벨의 의존성을 제공하는 ApplicationComponent(현재는 SingletoneComponent로 변경됨)가 생성 된다고 합니다. 

그리고 ApplicationComponent 생성후 
@AndroidEntryPoint가 붙은 클래스를 통해 의존성을 제공하는 Component들을 생성한다고 하는데요,

여기서 ApplicationCompent도 의존성을 제공하고 그 하위 Sub Component도 종속성을 제공한다고 하는데

제 생각에는 Sub Component가 독자적으로 의존성을 제공하는게 아니라 Application Compnent에서 부터

상속개념?으로 의존성을 받아서 다시 Sub Component에서 받은 의존성을 제공하는 개념인것같은데..

맞나요?

 

2. Google Developers Korea Blog: MAD Skills 시리즈 - Hilt를 소개합니다 (googleblog.com)

위 링크 설명에서 '이 예제에서는 MusicPlayer를 PlayActivity에 주입합니다.' 라고 되어있는데요,

MusicPlayer가 주입'하는'게 아니라 PlayerActivity로부터 주입 '받는' 것아닌가요?

@AnroidEnryPoint가 의존성을 제공한다고 했으니까요..

 

3. https://www.youtube.com/watch?v=gkUCs6YWzEY

10:43 ~ 11:30 을 보시면 ApplicationComponent 내에 DataModule가 있고 그 내부에 

MemoRepository가 있는데 DataModule 코드 내부에는 MemoRepository 관련 코드가 없는데 어떻게 존재 하는건가요?

 

4. @AnrdoidEntryPoint가 의존성을 제공한다고 했잖아요?

만약에 위 사진처럼 필드에 @Inject 어노테이션이 붙어있는 필드가 있을때,

의존성 주입 과정이  컴파일 과정에서 이 객체의 클래스의 @Inject 생성자 어노테이션을 보고

Hilt에 이 클래스의 객체 생성방법을 제공하고 Hilt는 이 클래스의 객체를 생성하고

@AnroidEntryPoint는 이 객체를 @inject 어노테이션이 붙어있는 필드에 의존성을 주입을 하는건가요?

codeslave (3,940 포인트) 님이 2022년 4월 20일 질문

1개의 답변

0 추천

Hilt에 대해서 잘 아는 건 아니지만, 질문하신 내용과 관련해서 말씀드리면, Hilt는 Annotation을 사용하여 컴파일시에 히든 클래스들을 생성합니다. Gradle에 kapt이용해서 어노테이션들을 처리합니다. @HiltAndroidApp은 Application class가 정의된 Hilt 그래프를 초기화하는 코드를 가지게 되고 Application 클래스에 필요한 injection을 수행합니다. Hilt가 만든 히든 클래스들은 Hilt의 플러그인이 런타임에 적절하게 실행될 수 있도록 해줍니다.

@AndroidEntryPoint의 경우는 Hilt에 의해 생성된 클래스를 살펴보면 좋을 것 같습니다.

아래는 MainActivity에 @AndroidEntryPoint 어노테이션을 사용했을 때 생긴 클래스입니다.

package com.mpark.citizenship;

import android.content.Context;
import androidx.activity.ComponentActivity;
import androidx.activity.contextaware.OnContextAvailableListener;
import androidx.lifecycle.ViewModelProvider;
import dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories;
import dagger.hilt.android.internal.managers.ActivityComponentManager;
import dagger.hilt.internal.GeneratedComponentManagerHolder;
import dagger.hilt.internal.UnsafeCasts;
import java.lang.Object;
import java.lang.Override;
import javax.annotation.processing.Generated;

/**
 * A generated base class to be extended by the @dagger.hilt.android.AndroidEntryPoint annotated class. If using the Gradle plugin, this is swapped as the base class via bytecode transformation.
 */
@Generated("dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator")
public abstract class Hilt_MainActivity extends ComponentActivity implements GeneratedComponentManagerHolder {
  private volatile ActivityComponentManager componentManager;

  private final Object componentManagerLock = new Object();

  private boolean injected = false;

  Hilt_MainActivity() {
    super();
    _initHiltInternal();
  }

  Hilt_MainActivity(int contentLayoutId) {
    super(contentLayoutId);
    _initHiltInternal();
  }

  private void _initHiltInternal() {
    addOnContextAvailableListener(new OnContextAvailableListener() {
      @Override
      public void onContextAvailable(Context context) {
        inject();
      }
    });
  }

  @Override
  public final Object generatedComponent() {
    return this.componentManager().generatedComponent();
  }

  protected ActivityComponentManager createComponentManager() {
    return new ActivityComponentManager(this);
  }

  @Override
  public final ActivityComponentManager componentManager() {
    if (componentManager == null) {
      synchronized (componentManagerLock) {
        if (componentManager == null) {
          componentManager = createComponentManager();
        }
      }
    }
    return componentManager;
  }

  protected void inject() { // <---- 요기
    if (!injected) {
      injected = true;
      ((MainActivity_GeneratedInjector) this.generatedComponent()).injectMainActivity(UnsafeCasts.<MainActivity>unsafeCast(this));
    }
  }

  @Override
  public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    return DefaultViewModelFactories.getActivityFactory(this, super.getDefaultViewModelProviderFactory());
  }
}

inject()메소드가 존재하고 이 메소드는 MainActivity의 onCreate바로 직전에 실행이 될 겁니다. 이때 MainActivity에 있는 @Inject 어노테이션이 붙어 있는 멤버 변수들에 대해 injection 을 수행하는 것이죠.  injection을 실제로 수행하는 클래스도 별도로 생성이 됩니다. 그리고 MainActivity에 필요한 depencey graph는 ActivityComponentManager타입의 componentManager란 녀석이 진행합니다. 따라서 상속은 아닌 걸로 보이고 Activity에 해당하는 의존관계에 따라 읜존관계 그래프를 만든다고 보여집니다.

3번 질문의 DataModule에 MemoRepsotiry가 존재하지 않아도 동작을 하는 이유는 

@Singleton
class MemoRepository @Inject constructor()

처럼,  @Inject 어노테이션을 사용하기 때문입니다.  MemoRepository 클래스는 Activity, Fragment, Service같은 안드로이드 시스템에 종속된 (생성자를 외부에서 호출하지 않는 라이프 사이클 컴포넌트) 클래스가 아니므로, 이런 클래스는 @Inject 어노테이션을 사용하는 것으로 @AndroidEntryPoint와 같은 역할을 할 수있습니다. 마찬가지로 kapt의 매직같은 거죠.

프로젝트가 간단한 경우는 굳이 Hilt같은 프레임워크를 사용할 필요가 없습니다. 특히 코틀린 같은 경우는 object 클래스 한 두개만 있으면 간단한 DI를 구현할 수 있습니다.

spark (227,830 포인트) 님이 2022년 4월 20일 답변
라이브러리 사용법만 알면되지 따로 공부해야하나 싶기도한데.. 그래도 이왕 사용하는거 개념을 알아야 어떻게 사용하고 알것같아서 공부하고는 있는데 좀 어렵네요ㅠㅋㅋ 영문서쪽이면 더 많이 영상이나 자료가 더 많이 있을듯한데 영어가 좀 약해서 시간상 쫓기다보니 오래도 걸리고..

암튼 감사합니다!
그래도 영어실력이 나아질 수록 할 수 있는게 훨씬 많습니다. 한글자료와는 비교가 안됩니다. 접근할 수 있는 자료의 양과 질이 달라요. 예를 들어, 트윗터의 트윗을 받는 것 만으로도 엄청난 정보를 얻을 수 있구요. 한국 구글 IO자료도 봤는데, 구글 본사가 진행하는 것과는 양과 질에서 많은 차이가 나더군요. 개인적으로 개발자료는 가능한 영어로된 것을 보시라고 권하고 싶네요.
문서는 전문용어가 많이 나오고 비슷한 패턴의 문장구조가 반복되므로 몇개월이면 익숙해 집니다. 충분히 투자할만한 가치가 있습니다. 아마 영어문서 읽는게 편해지시면, 다른 세계가 보이실 겁니다.
...