본문 바로가기

안드로이드

의존성주입 프레임워크 (dagger, dagger2)

1. 의존성 주입 프레임워크가 왜 필요할까?


    (1) 의존성 파라미터를 생성자에 작성하지 않아도 되니 보일러 플레이트 코드가 줄어든다. 

    (2) 인터페이스에 구현체를 쉽게 교체하면서 상황에 따라 적절한 행동을 정의할수있다. -> 테스팅을 쉽게 할 수 있다. 


2. dagger


 - DI Framework(의존성주입 프레임워크)이며 Square사의 dagger와 Google의 dagger2가 있다. 

    의존성주입이란 외부에서 의존객체를 생성해서 넘겨주는 것을 의미한다. 예를 들어 a class가 b class를 의존할때 b object를 a가 직접 생성하지 않고

    외부에서 생성하여 넘겨주면 의존성을 주입했다고 한다. 


    (2) dagger1 - square


      다양한 인젝션 지점, 다양한 바인딩, 다양한 모듈, 다양한 객체 그래프


      Dagger1 컴파일 시간에 바인딩을 할뿐만아니라 리플렉션도 사용합니다. 객체를 인스턴스화 할필요가 없더라도, 그래프 합성을 위해서 

      리플렉션을 사용합니다. Dagger 는 모두가 적합한 방법을 찾아내는 것을 런타임시에 진행합니다. 

      그래서 가끔 비효율성이 발생하고 디버깅할 때 어려움이 있기도 합니다.


    (3) dagger2 - google


        Dagger 2 는 의존성을 만들고 제공하는 코드를 직접 쓰는걸 코드 생성으로 대체하자는 아이디어를 가지고 있습니다.

       이전 버전과 비교했을 때 많은 부분이 비슷하지만 중요한 차이점들이 있습니다.

       리플렉션을 전혀 사용하지 않습니다. 그래프 유효성, 환경설정, 전제조건들을 컴파일 타임에 검사합니다.

       쉽게 디버깅 할 수 있고 추적 가능합니다: 의존성 제공과 생성에 대한 전체 call stack 을 볼 수 있습니다. 

       더 효율적입니다: 구글에 따르면 13% 의 성능 향상이 있다고합니다.

       코드 난독화: 손으로 쓴 코드 처럼 ‘method dispatch’ 를 사용합니다.

       물론 이런 좋은 기능들은 비용을 수반하고 덜 유연하게 만듭니다. 예를들면 리플렉션을 사용하지 않아서 ‘dynamism’ 이 없습니다.

       리플렉션이란 객체를 통해 클래스의 정보를 분석해 내는 프로그램 기법을 말한다.

      @Inject: 의존성을 주입을 요청한다. inject 어노테이션으로 의존성 주입을 요청하면 연결된 component가 module로부터 객체를 생성하여 넘겨준다. 

      @Component: @Inject 와 @Module 사이 다리이며 의존성을 주입하는 역할을 합니다. 컴포넌트는 미리 정의한 모든 타입의 인스턴스를 줍니다.    @Component 어노테이션은 인터페이스에다만 달아야합니다 그리고 컴포넌트를 구성하는 모든 @Module 이 달린 클래스 목록을                            적어야합니다. 컴포넌트에서 사용하는 모듈들중 하나라도 없다면 컴파일 타임에 에러를 만듭니다. 모든 컴포넌트들은 컴포넌트에                            포함된 모듈들을 통해 의존성의 범위를 알 수 있습니다.

  • @Subcomponent : component는 계층 관계를 생성할 수 있다. inject를 요청받으면 subcomponent로부터 먼저 의존성을 검색하고 없으면 부모로 올라가면서 검색한다. 
  • @Module: component에 연결되어 의존성 객체를 생성한다. 생성 후에 scope에 의해 관리되기도 한다. 
  • @Provide: 의존성 객체를 생성할 메소드 정의
  • @Scope: 생성된 객체의 lifecycle범위로 module에서는 scope를 보고 객체를 관리한다. 
  • @Qualifier: 클래스의 유형이 종속성을 식별하기 불충분할 때 사용하는 어노테이션입니다. 예를 들어 안드로이드의 경우, 많은 경우 컨텍스트의 다양한 타입이 필요합니다, 그래서 “@ForApplication”, “@ForActivity” 같은 식별자 어노테이션을 정의합니다. 컨텍스트를 주입할 때 이 식별자 어노테이션을 이용해서 Dagger가 어떤 타입을 제공할지 정해줍니다.


3. dagger2의 문제점 : activity를 만들 때마다 build() 하고 inject() 해주는 보기 안 좋은 boilerplate들이 많아져서 Gregory Kick이란 분께서 Dagger2의 새로운 사용방법을 제시 

    (1) ActivityBindingModule : 각각의 Acitivity에 대하여 전부 모듈을 만들어서 하나하나씩 Component에서 선언해주면 지저분해지니까, Activity와 Activity 모듈을 각각 만들고 이를 합쳐주는 module을 하나 또 만든다.

@Module

public abstract class ActivityBindingModule {

    @ActivityScoped

    @ContributesAndroidInjector(modules = TasksModule.class)

    abstract TasksActivity tasksActivity();


    @ActivityScoped

    @ContributesAndroidInjector(modules = AddEditTaskModule.class)

    abstract AddEditTaskActivity addEditTaskActivity();


    @ActivityScoped

    @ContributesAndroidInjector(modules = StatisticsModule.class)

    abstract StatisticsActivity statisticsActivity();


    @ActivityScoped

    @ContributesAndroidInjector(modules = TaskDetailPresenterModule.class)

    abstract TaskDetailActivity taskDetailActivity();

}

    (2)  AppComponent : AppComponent 딴에서 공급자를 Builder 해줌. 그럼 Application에서 전역적으로 가져다 쓸 수 있다.


@Singleton

@Component(modules = {TasksRepositoryModule.class,

        ApplicationModule.class,

        ActivityBindingModule.class,

        AndroidSupportInjectionModule.class})

public interface AppComponent extends AndroidInjector<ToDoApplication> {

    

    @Component.Builder

    interface Builder {


        @BindsInstance

        AppComponent.Builder application(Application application);


        AppComponent build();

    }

}


(3) Application : Application에서 이런식으로 build. extends DaggerApplication!


public class ToDoApplication extends DaggerApplication {


    @Override

    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {

        return DaggerAppComponent.builder().application(this).build();

    }

}


(4) ActivityModule : Activity 모듈에는 필요한 애들을 선언해준다. 여기서 참고로 @Binds는 @Provides에서 parameter를 그대로 return하는 과정을 줄여준다.


@Module

public abstract class StatisticsModule {


    @FragmentScoped

    @ContributesAndroidInjector

    abstract StatisticsFragment statisticsFragment();


    @ActivityScoped

    @Binds

    abstract StatisticsContract.Presenter statisticsPresenter(StatisticsPresenter presenter);

}


   (5) Activity : DaggerAcitivty를 상속받아서 Activity를 생성하면 된다. 


    public class StatisticsActivity extends DaggerAppCompatActivity {


    @Inject

    StatisticsPresenter mStatiticsPresenter;

    @Inject

    StatisticsFragment fragment;


 1. DaggerApplication을 상속한다. 얘가 필수적인 dispatcher들을 주입 dispatcher : 먼저 수행되야 할 작업을 선택해서 공급해주는 놈이라고 한다

2. AndroidSupportInjectionModule.class를 AppComponent에 선언해주어야된다.

3. @ContributesAndroidInjector 얘로 bind를 해준다.