Dagger2를 알아보자 – 기본편
Dagger2를 알아보자 – Scope 
Dagger2를 알아보자 – Injection의 종류 
Dagger2를 알아보자 – Qualifier 
Dagger2를 알아보자 – Binding 
Dagger2를 알아보자 – Multibinding 
Dagger2를 알아보자 – SubComponent 
Dagger2를 알아보자 – Android (You’re here)
Dagger2를 알아보자 – Testing(준비중)
Dagger2를 알아보자 – Dynamic Feature에 적용하기


대부분의 다른 의존성 주입 프레임 워크에 비해 Android를 위한 Dagger의 주요 장점 중 하나는 리플렉션 없이 애노테이션 프로세싱을 이용하여 자바소스를 생성한다는 것입니다. 

안드로이드용 Dagger는 다른 애노테이션을 쓰기 때문에 앞서 다루었던 방법과는 조금 다르나 기본적인 원리는 같습니다.

Dagger를 사용하여 안드로이드 애플리케이션 코드를 작성하는것에 대한 가장 큰 어려움은 액티비티의 인스턴스를 안드로이드 OS 내에서 만든다는 것입니다. 다시 말해 개발자가 new Activity(…) 를 할 수 없는 것입니다. 그렇기 때문에 생명주기에서 반드시 멤버 인젝션이 이루어져야하고 그 코드는 아래와 같습니다.

public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 반드시 선행되어야 할 작업, 그렇지 않으면 frombulator는 null이 됩니다.
    ((SomeApplicationBaseType) getContext().getApplicationContext())
        .getApplicationComponent()
        .newActivityComponentBuilder()
        .activity(this)
        .build()
        .inject(this);
    // 인젝션이 끝났으므로 신나는 코딩 이예~
  }
}

위의 코드는 정상적으로 동작 하겠지만, 몇가지 문제점을 가지고 있습니다.

  • 예를들어 위의 코드처럼 인젝션을 하기 위해 애플리케이션을 통한 액티비티 컴포넌트를 얻어 빌드 후 인젝션 하는 단순 반복되는 보일러플레이트 코드는 나중에 리팩토링 할 때 큰 고통을 안겨줍니다. 
  • 좀 더 근본적으로는, 액티비티 클래스에 맞는 인젝션이 이루어져야 합니다. 이것이 구체적인 자료형 대신 인터페이스를 통해 수행되는 경우에도 종속성 주입의 핵심 원칙이 깨집니다. 즉, 클래스가 주입되는 방식에 대해 알지 않아야합니다.

안드로이드용 Dagger는 이러한 패턴을 단순화해주는 방법을 제공해주고 있습니다.

액티비티에 Injection하기

  1. 안드로이드용 Dagger를 사용하기 위해 AndroidInjectionModule을 애플리케이션 컴포넌트에 포함시킵니다.
  2. AndroidInjector<YourActivity>를 상속한  @Subcomponent 클래스를 만들어야합니다. Subcomponent는 @Subcomponent.Factory를 만들어야 하는데 이때 AndroidInjector.Factory<YourActivity>를 상속합니다.

    그러면 다음과 같은 모양새의 코드가 나오게 됩니다.

    @Subcomponent(modules = ...)
    public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
      @Subcomponent.Factory
      public interface Factory extends AndroidInjector.Factory<YourActivity> {}
    }
  3. Subcomponent를 정의한 뒤에는 모듈에 추가시킵니다. 그런뒤 모듈내에서 subcomponent의 factory를 @Binds와 @IntoMap을 사용하여 map으로 멀티바인딩하고 액티비티를 클래스 키로 지정합니다. Subcomponent가 포함된 해당 모듈을 컴포넌트에 추가시킵니다. 그러면 Application 스코프내에서 해당 subcomponent factory를 주입받을 수 있게 됩니다.
    @Module(subcomponents = YourActivitySubcomponent.class)
    abstract class YourActivityModule {
      @Binds
      @IntoMap
      @ClassKey(YourActivity.class)
      abstract AndroidInjector.Factory<?>
          bindYourAndroidInjectorFactory(YourActivitySubcomponent.Factory factory);
    }
    
    @Component(modules = {..., YourActivityModule.class})
    interface YourApplicationComponent {}

    꿀팁 : 만약 subcomponent와 factory가 아무런 메소드 또는 부모를 상속하지 않는다면, 2,3번 대신에 @ContributesAndroidInjector 애노테이션을 사용하면 위의 코드들을 자동으로 생성해줍니다. 해당 애노테이션을 사용하는경우에 abstract 모듈이여야 하며 액티비티를 반환하는 메소드에 애노테이션을 붙여야합니다. 만약 subcomponent가 scope가 필요하다면 scope도 추가 할 수 있습니다.

    @ContributesAdnroidInject코드는 다음과 같습니다.

    @ActivityScope
    @ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
    abstract YourActivity contributeYourAndroidInjector();
  4. 다음은 HasAndroidInjector를 Application에 구현하고 DispatchingAndroidInjector<Object>를 주입받아 androidInjector() 메소드에서 반환하는 것입니다.
    public class YourApplication extends Application implements HasAndroidInjector {
      @Inject DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
    
      @Override
      public void onCreate() {
        super.onCreate();
        DaggerYourApplicationComponent.create()
            .inject(this);
      }
    
      @Override
      public AndroidInjector<Object> androidInjector() {
        return dispatchingAndroidInjector;
      }
    }
  5. 마지막으로 Activity.onCreate() 메소드에서 AndroidInjection.inject(this)를 super.onCreate(); 전에 호출하세요.
    public class YourActivity extends Activity {
      public void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
      }
    }

어떻게 이게 동작 하냐구요?

AndroidInjection.inject()를 호출 하면 애플리케이션으로부터 DispatchingAndroidInjector<Object>를 얻게되고 해당 액티비티를 인자로 메소드 인젝션 하게 됩니다. inject(YourActivity);

프레그먼트의 경우 onAttach()에서 인젝션합니다.

모듈에 정의된 액티비티들과는 달리 프레그먼트의 경우 어느 모듈에 설치할치 선택권이 있습니다. 다음과 같이 프레그먼트의 컴포넌트를 만들 수 있습니다.

  • 다른 프레그먼트 컴포넌트의 서브컴포넌트로 만든다.
  • 액티비티 컴포넌트의 서브컴포넌트로 만든다.
  • 애플리케이션 컴포넌트의 서브컴포넌트로 만든다.

프레그먼트 컴포넌트를 어디에 위치할 지 결정하고 나면, 프레그먼트를 사용하는 곳에서 HasAndroidInjector를 구현합니다. 만약에 프레그먼트를 YourActivity의 서브컴포넌트로 지정했다면 코드는 다음과 같을 겁니다.

public class YourActivity extends Activity
    implements HasAndroidInjector {
  @Inject DispatchingAndroidInjector<Object> androidInjector;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
    // ...
  }

  @Override
  public AndroidInjector<Object> androidInjector() {
    return androidInjector;
  }
}

public class YourFragment extends Fragment {
  @Inject SomeDependency someDep;

  @Override
  public void onAttach(Activity activity) {
    AndroidInjection.inject(this);
    super.onAttach(activity);
    // ...
  }
}

@Subcomponent(modules = ...)
public interface YourFragmentSubcomponent extends AndroidInjector<YourFragment> {
  @Subcomponent.Factory
  public interface Factory extends AndroidInjector.Factory<YourFragment> {}
}

@Module(subcomponents = YourFragmentSubcomponent.class)
abstract class YourFragmentModule {
  @Binds
  @IntoMap
  @ClassKey(YourFragment.class)
  abstract AndroidInjector.Factory<?>
      bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Factory factory);
}

@Subcomponent(modules = { YourFragmentModule.class, ... }
public interface YourActivityOrYourApplicationComponent { ... }

사실 기본적으로 위의 코드를 사용하지 않는방법도 있습니다. 

DispatchingAndroidInjector는 적절한 AndroidInjector.Factory를 특정클래스에서 런타임에 찾기 때문에, Base 클래스에 HasAndroidInjector나 AndroidInjection.inject()를 구현할수 있습니다. 모든 서브클래스들은 적당한 @Subcomponent를 바인딩하는것을 필요로 합니다.

Dagger는 DaggerActivity나 DaggerFragment와 같은 몇몇 Base 타입을 제공하고 있습니다. 만약 클래스의 계층이 복잡하지 않다면 제공하는 Base클래스를 사용하는것이 좋은 선택입니다. 마찬가지로 DaggerApplication클래스도 제공하고 있는데 이 클래스를 사용하는 경우 개발자가 할일은 applicationInjector()를 override하여 애플리케이션 컴포넌트를 반환하는 것 뿐입니다.

기타 DaggerService, DaggerIntentService, DaggerBroadcastReceiver, DaggerContentProvider도 제공하고 있습니다.

DaggerBroadcastReceiver는 메니페스트에 등록된 경우에만 사용해야하며, 리시버를 직접 생성하는 경우에는 생성자 인젝션을 하는편이 더 좋습니다.

TL;DR

이전 포스팅을 참조하지 않고 이 글부터 읽으신분은 내용을 이해하기 어렵습니다. 시간을 내서 이전 Dagger2를 알아보자 시리즈를 읽어보시는걸 추천합니다. 이미 이전 글들을 다 읽고 오신분이고, 단지 프로젝트에 Dagger2를 어떻게 적용할지 고민 하고 계시다면 안드로이드에서 Dagger2 적용하기를 참고하시면 예제와 패턴을 제공하고 있으니 참고하시면 좋습니다.

카테고리: 미분류

2개의 댓글

지빙 · 2019년 12월 11일 2:21 오후

좋은 글 잘 봤습니다.

    Charlezz · 2019년 12월 11일 5:02 오후

    감사합니다

답글 남기기

Avatar placeholder

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