[Android] Dagger2 정리

의존성?

  • 의존성?
    • 코드에서 두 모듈간의 연결
    • 두 클래스 간의 관계
    • 의존성이 크다는 것은 Coupling(결합도)가 높다는 것
  • 의존성이 왜 중요한가?
    • 하나의 모듈이 변경됨에 따라 결합된 다른 모듈이 영향을 받게 된다.
    • 두개의 모듈일때는 괜찮지만 최악의경우 모듈이 100개,1000개…n 개 일때 하나의 모듈변경으로 인해 n-1개의 모듈이 영향을 받는다고 생각해보자
    • 나머지 모듈이 제대로 동작하는지에 대한 검증이 필요할 수도 있다. 그럼 시간과 비용도 n만큼??
    • 결합도가 높으면 독립성이 떨어진다. 반대로 결합도가 낮으면 독립성이 높아진다.
  • 의존성 주입

    • 구성요소간의 의존 관계가 소스코드 내부가 아닌 외부의 설정파일
      등을 통해 정의되게 하는 디자인 패턴

    • 외부에서 인스턴스 변수를 생성하여 넘겨주는(주입하는) 것

    • 객체가 자신이 사용할 객체를 스스로 선택하지 않고
      제 3의 객체에게 사용될 객체를 주입받는다.(의존 관계 역전)

      1
      2
      3
      public Example(DatabaseThingie useThisDatabaseInstead) {
      myDatabase = useThisDatabaseInstead;
      }
    • useThisDatabaseInstead 라는 다른 클래스의 인스턴스를 사용하게 만듬

    • Example 클래스에 “의존성” 을 “주입” 했다

  • 의존성 주입의 목적
    • 독립된 모듈에 대한 테스트 코드 작성이 가능하다.
    • 하나의 모듈이 변경되어도 다른 모듈들이 영향을 받지 않는다.
    • 객체 생성을 외부에서 하므로 재사용성이 높아진다.

Dagger?

  • Dagger : Dependency Injection (의존성 주입) Framework
  1. Provides 메소드로 이루어진 Module 을 작성한다.
  2. Component 를 작성해서 Module 과 연결하고, 의존성 객체를 주입받을 객체를 만든다.
  • 객체를 일단 만들고, 모듈로 객체를 사용하는 방법을 정의하고,
    컴포넌트로 모듈을 메인으로 사용할 클래스에 전달한다.

  • Dagger 의 Inject 방법

    • Constructor Injection (생성자 주입)
    • Method(Setter) Injection (메소드 주입)
    • Field Injection
  • Provides
    • 어노테이션이 달린 메소드를 Dagger Pool 에 제공하겠다는 의미
    • 모든 Provides 메소드는 @Module 클래스 안에 속해야 함
    • Binds
      • Provides 와 같지만 매개변수를 바로 return 한다
      • Provides 의 개선된 버전
    • Multibinding
      • Set 이나 Map 을 이용한 Multibinding 을 지원함
1
2
3
4
5
6
7
8
9
10
11
@Provides
public LoginContract.Presenter
provideLoginPresenter(LoginPresenter loginPresenter) {
return loginPresenter;
}

---------------------------------------------------------------

@Binds
public abstract LoginContract.Presenter
provideLoginPresenter(LoginPresenter loginPresenter);
  • Scope
    • 해당 클래스의 단일 인스턴스가 존재하는 범위
    • Singleton scope
      • Application scope 를 가지는 annotation
      • 싱글톤 scope 를 특정하게 지정해주면 아무 곳에서나 inject 할 수 있다.
  • Module
    • 의존성 객체를 생성함
  • Inject
    • 의존성 주입을 요청하는 Annotation
    • @Inject 를 사용하면, ComponentModule 을 이용해 Generate 한 객체들이
      Inject Annotation 이 달린 함수에 주입된다.
  • Component
    • 구성요소 라는 뜻. Application Component 는 어플리케이션 구성요소이다.
    • 이 annotation 이 달려있는 인터페이스는 의존성 주입 코드가 generate 된다
    • module class 에서 제공받은 객체를 어떤곳에 주입 할지 정하는 역할을 한다.
      • Activity 에서 build 를 통해 받을 곳이라는 명시를 해야 한다
    • Subcomponent
      • 부모 component 가 존재하는 component
      • 코드 생성은 부모 component 에서 이루어진다
      • 사용법은 interface 나 abstract class 에 @Subcomponent 를 달아줌
  • Qualifier
    • 같은 Return 값을 가질 때 구별하기 위한 annotation
      • 같은 값을 return 하는 경우 @other1 @other2 로 구분
        이 때 Inject 할때도 @other1 @other2 를 붙여서 구분해서 inject 가능

안드로이드에서의 사용법

  • Dagger2 코딩하기
    1. Dagger2 를 빌드할 Activity 를 만들기
    2. 전체를 총괄할 AppComponent 만들기

1. 애플리케이션에서 Dagger 빌드하기

  • 우리가 첫 공부할때 Main 문에서 하듯 Dagger2 의 실행부터 역으로 살펴보기로 한다.

  • Activity 의 인스턴스를 Android OS 내에서 만들기 때문에 (new Activity 가 불가능)
    Life Cycle 내에서 반드시 Constructor Injection 이 아닌 Field Injection 이 이루어져야 한다.

  • 과거의 방식

    1
    2
    3
    4
    5
    ((MyApplication) getApplication())
    .getAppComponent()
    .myActivity(new MyActivityModule(userId))
    .build()
    .inject(this);
  • 최근 방식

    • Class 가 어떤 방식으로 의존성 주입이 되는지 전혀 알 필요 없게 구성되었다.
      1
      2
      3
      4
      5
      DaggerAppComponent
      .builder()
      .application(this)
      .build()
      .inject(this);

2. AppComponent 로 전체 모듈 구성하기

  • 빌드할 메인 에플리케이션을 만들었으면, 빌드에 필요한 재료를 만든다.
  • AppComponent interface 는 module 들을 Component 라는 영역에 가두고,
    Builder 로 Component 영역에 접근 할 수 있게 만든다.
  • 빌드 할 때 Component Annotation 에 기입된 모듈에 있는 모든 클래스는 인스턴스가 생성된다.
  • @Component.Builder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Singleton
@Component(modules = [
(AndroidInjectionModule::class),
(AppModule::class),
(ActivityBuilder::class)
])
interface AppComponent {

@Component.Builder
interface Builder {

@BindsInstance
fun application(application: Application): Builder

fun build(): AppComponent
}

fun inject(app: MvpApp)

}

3. AppModule 로 전체 객체 구성하기

4. 사용할 Activity 구성하기

  • MVP 구성
    • interactor 폴더
      • [Activity 이름]Interactor
      • [Activity 이름]MVPInteractor
    • presenter 폴더
      • [Activity 이름]Presenter
      • [Activity 이름]MVPPresenter
    • view 폴더
      • [Activity 이름]Activity
      • [Activity 이름]MVPView
    • [Activity 이름]ActivityModule
  • Module 에서 Model 역할인 Interactor 와 Presenter 을 Provides 함
  • activityDispatchingAndroidInjector: DispatchingAndroidInjector
    • 제네릭 타입으로 클래스를 선언함으로써 객체를 선언할때 강한 TypeCheck 를 하고 객체 생성 시타입변환(Casting) 을 하지 않아도 된다.
    • @Inject Annotation 을 사용함으로써 Generate 되어있던
  • Reflection 없이 사용하기
    • Reflection : 자바 언어의 기능 중 하나로 프로그램 내부 속성을 조작 할 수 있게함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
// 인젝션이 끝났으므로 신나는 코딩 이예~
}
}
  • 빌드 후 인젝션하는 단순 반복되는 보일러플레이트코드가 너무 많다.
  • 종속성 주입의 원칙인 클래스가 주입되는 방식에 대해서 알지 않아도 되게 만들어야한다.
  • AndroidInjection
    • AndroidInjection.inject() 를 호출 하면 애플리케이션으로부터
      DispatchingAndroidInjector<Object> 를 얻게되고 해당 액티비티를 인자로 메소드 인젝션 하게된다.
    • @Binds 나 @Provider 을 설정하지 않아도 Activity 와 바인딩 할 수 있다.
1
private fun performDI() = AndroidInjection.inject(this)

기타

  • @ContributesAndroidInjector
    • 반환타입을 통해 AndroidInjector 를 생성시켜주는 인터페이스
    • Component 에서 Module 을 Activity 에 Inject 시켜주는 일을 간단히 하게 해준다.
  • Dagger 에서는 항상 “객체” 를 Provide 하고, “객체” 를 Inject 해서 사용한다
공유하기