Skip to content

FaithDeveloper/SampleDaggerForAndroid

Repository files navigation

Android Dagger 활용하기

Dagger 의 기본 개념을 이해하고 있다는 것을 전재로 Android에서 Dagger 을 사용하는 방법을 공유하겠습니다. 만약 Dagger 개념에 대해서 이해 못하신 분은 Dagger 간단히 알아보기 포스트를 참고해주세요.


Android Studio에서 Dagger 사용 시 Dependency 을 설정해야 합니다.

//dagger2
implementation 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
implementation 'com.google.dagger:dagger-android:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
  • com.google.dagger:dagger-android dagger 에서 android Library 을 활용할 수 있는 라이브러리 입니다.
  • com.google.dagger:dagger-android-support dagger 에서 android support Library 을 활용할 수 있는 라이브러리 입니다.

안드로이드 프레임워크에서  Application 은 Singleton 으로서의 역할을 하며 컴포넌트들은 각각 고유의 라이프사이클을 기반으로 동작합니다. Application는 inject 를 수행하는 시작점이 됩니다. 따라서 Dagger 는 Application 단위에서 @Component 를 컴포넌트 단위에서 @Subcomponent 를 구성하고 inject 를 하는 것을 가이드로 주고 있습니다. 이것을 간단하게 하기 위해서 dagger.android 클래스들을 제공하고 있습니다.

안드로이드 컴포넌트

  • Application
  • Activity
  • Service
  • Content Provider
  • Broadcast

MainActivity Presenter DI 구현 방법

MVP 패턴 (Model-View-Presenter) 으로 구성한 MainActivity의 Presenter를 DI 구현하는 방법을 공유하겠습니다.

MainAcitivy

public class MainActivity extends AppCompatActivity implements MainActivityContract.View {

	MainActivityContract.Presenter presenter;
	
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Application는 component , MainActivity 는 subcomponent 을 구성하게 됩니다. MainActivity에서는 inject 역할을 담당해줄 @Subcompoent 와 외존성을 설정하는 @Module 을 구성합니다.


MainModule.class

@Module(subcomponents = MainFragmentComponent.class)
abstract class MainModule {
    @ActivityScope
    @Binds
    abstract MainActivityContract.View bindView(MainActivity activity);


    @ActivityScope
    @Binds
    public abstract MainActivityContract.Presenter bindPresenter(MainPresenterImpl mainPresenter);
}

ActivityScope.class

@Scope
public @interface ActivityScope {
}

MainComponent.class

@ActivityScope
@Subcomponent(modules = MainModule.class)
public interface MainComponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity>{
    }
}

@Subcomponent annotation 을 붙여주고 의존성 설정을 위해 MainModule 을 설정합니다. MainComponent.class 에서 주의 깊게 살펴볼 것은 AndroidInject 를 상속하는 것입니다. AndroidInject 을 상속받으면서 interface로 MainComponent 을 구성하고 @Subcomponent.Builder 작성해주면 @Subcomponent 작성이 끝납니다.

AndroidInject

AndroidInjectdagger.android 에 포함하고 있는 클래스로 @Subcomponent 의 코드inject 절차를 간소화 합니다.  안드로이드에서는 프래임워크 LifeCycle 을 보면 앱 실행 시 Application 부터 시작하게 됩니다. 그렇기에 Application에서 @Component 를 구성하게 됩니다. @Subcomponent 인 Activity 에서 inject를 할 경우 Component 호출 -> SubComponent 빌드 -> inject 하는 코드들을 작성해야 합니다.  이것을 AndroidInjector.inject 한줄로 간소화 시켜줍니다.

  • AndroidInjector.inject 구성 안한 SubComponent 사용 예시
public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // DO THIS FIRST. Otherwise frombulator might be null!
    ((SomeApplicationBaseType) getContext().getApplicationContext())
        .getApplicationComponent()
        .newActivityComponentBuilder()
        .activity(this)
        .build()
        .inject(this);
    // ... now you can write the exciting code
  }
}
  • AndroidInjector.inject 구성한 SubComponent 사용 예시
public class YourActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
  }
}

Application

Application 에서 ComponentModule 을 구성하게 됩니다. 이유는 Application의 Lifecycle 에 따라 Component 는 Singleton 으로 관리 되어야하며 inject 또한 Application에서 이뤄지기 때문입니다. Module 안에 구성 또한 Application Lifecycle 을 적용하게 되어 Singleton 으로 적용 됩니다.

AppComponent.class

@Singleton
@Component(modules = {
        AndroidInjectionModule.class,
        AppModule.class
})
public interface AppComponent {
    void inject(DaggerSampleApp daggerSampleApp);
}

SigletonScope 로 구성하였으며 Application 에서 inject 발생하므로 members-injection 메서드 구성 하였습니다. 또한 AndroidInjectionModule.classModules 로 설정하면서 dagger.android 에서 제공하는 Class을 사용할 수 있습니다.


AppModule.class

@Module(subcomponents = MainComponent.class)
abstract class AppModule {
    @Singleton
    @Binds
    abstract DataSource bindDataSource(DataSourceImpl dataSource);

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivity(MainComponent.Builder builder);
}

Android 에서 AndroidInjector 사용을 위해서는 AndroidInject.Factory Binds 을 추가로 만들어줘야 합니다. 또한 Activity 는 해당하는 @Subcomponent 를 통해 inject 되므로 Subcomponent.builder 를 파라미터로 받는 바인터를 만들어 줍니다. AndroidInjector.FactoryMultiBinds 동작이 되므로 @ActivityKey(dagger.android) 를 지원하고 있습니다. ( 다른 컴포넌트들에 대한 key annotation 도 지원합니다. 예시. FragmentKey)


Application, MainActivity Inject

DaggerSampleApp.class

public class DaggerSampleApp extends Application implements HasActivityInjector{
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        // inject
        DaggerAppComponent.create().inject(this);

    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingAndroidInjector;
    }
}

Application 에서 HasActivityInject 를 implement 해주면 AndroidInject<Activity> 를 리턴해주는 메서드를 작성할 수 있습니다. 리턴(return) 객체인 AndroidInject<Activity> 는 AndroidInjectModule 을 통해 바인딩(Binding) 할 수 있으며 이미 AppComponent 에서 modules 에 설정하여 install 을 하였습니다.

따라서 DispatchingAndroidInjector<Activity> 멤버변수를 만들고 @Inject 를 달아주면 inject 시에 Application Module 로 설정한 Activity 을 바인딩을 해줍니다.

onCreate 에서 AppComponent 생성 후 inject 를 실행시켜주는 코드를 작성하면 Application 설정은 마무리가 됩니다.


MainActivity.class

public class MainActivity extends AppCompatActivity implements MainActivityContract.View {

    @Inject MainActivityContract.Presenter presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AndroidInjection.inject(this);
    }
}

MainActivity 에서는 AndroidInjection.inject(this); 로 @Inject 멤버변수에 inject 를 수행합니다.

이러한 한줄 코드로 바인딩(Binding) 해주는 것 때문에 dagger을 사용하지 않았나 예상이 되는데요. AndroidInject 로 DI 구성한 Activity을 바인딩(Binding) 할 경우 기본적으로 바인더(Binds)를 생성하게됩니다. 따라서 별도로 @Binds@Provider 설정하지 않아도 Activity 를 바인딩(Binding) 할 수 있습니다.


PresenterImpl 구성

객체를 제공하려면 @Provides 또는 @Inject constructor(생성자) 로 제공하고 있습니다. MainPresenterImplMainActivity 에 제공 하기 위해서 @Inject constructor(생성자) 을 사용하면 다음과 같습니다.

public class MainPresenterImpl implements MainActivityContract.Presenter {
    MainActivityContract.View view;
    DataSource dataSource;

    @Inject
    public MainPresenterImpl(MainActivityContract.View view, DataSource dataSource){
        this.view =view;
        this.dataSource = dataSource;
    }
}

MainFragment DI 구현 방법

FragmentActivity 안에서만 동작하므로 부모가 Activity인 @Subcomponent 을 구성하게 됩니다. Fragment 도 Lifecycle 을 갖고 있기에 @FragmentScope 을 설정하였습니다.


FragmentScope.class

@Scope
public @interface FragmentScope {
}

MainFragmentComponent.class

@FragmentScope
@Subcomponent(modules = MainFragmentModule.class)
public interface MainFragmentComponent extends AndroidInjector<MainFragment> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainFragment> {
    }
}

Fragment 에서 AndroidInjectorinject 를 수행하려면 부모가 되는ActivityInjector를 생성해야 합니다. 생성 방법은 MainActivity 에서 설정하였던 방식과 동일합니다.

Fragment 에서 AndroidInjector 을 사용하여 inject을 수행하려면 Fragement 을 사용할 Activity에 Injector를 생성해야 합니다. Activity에 HasSupportFragmentInjectorimprement 하고 DispatchingAndroidInjector 을 바인딩(Binding) 받아 리턴합니다.


MainActivity.class

public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector, MainActivityContract.View {

    @Inject MainActivityContract.Presenter presenter;
    @Inject DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
    //...
}

Activity ModuleSubcomponent로 등록하고 AndroidInjector.Factory 에 바인딩 해줄 Component.Builder을 설정합니다. Activity에서도 언급했듯이 AndroidInjector.Factory 은 MultiBinding 을 하기에 FragmentKey로 사용하여 Key를 설정합니다.


MainModule.class

@Module(subcomponents = MainFragmentComponent.class)
abstract class MainModule {
    @ActivityScope
    @Binds
    abstract MainActivityContract.View bindView(MainActivity activity);

    @ActivityScope
    @Binds
    public abstract MainActivityContract.Presenter bindPresenter(MainPresenterImpl mainPresenter);

    @Binds
    @IntoMap
    @FragmentKey(MainFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment>
    bindMoviesFragment(MainFragmentComponent.Builder builder);
}

@FragmentKey 입력 시 주의사항

FragmentKeydagger.android.supportdagger.android 두 패키지에 모두 있습니다. 따라서 FragmentKey로 사용하려는 Fragment가 android.support.v4.app.Fragmentandroid.app.Fragment 패키지 중 소속되어 있는지 확인 후 알맞은 패키지를 import 해야 합니다.


Fragment Component 에서 AndroidSupportInjector을 사용하였기에 Fragment 에서는 inject 을 호출만으로 바인딩 할 수 있습니다.

public class MainFragment extends Fragment implements MainFragmentContract.View{

    @Inject MainFragmentContract.Presenter presenter;
  
    @Override
    public void onAttach(Context context) {
        AndroidSupportInjection.inject(this);
        super.onAttach(context);
    }
}

정리

DI 부터 시작해서 Dagger 와 Android Dagger에 대한 내용을 포스트 하였습니다. 의존성을 요청받으면 Subcomponent, Component, Inject 생성자 순으로 검색하여 주입하는 것을 확인 할 수 있습니다.

Dagger의 주요 기능은 Android Dager 에서 AndroidInjector 을 제공하여 간단하게 Inject 할 수 있는 것과 Scope 을 통하여 @AndroidScope, @FragmentScope 을 통하여 Lifecycle 에 맞춰서 설정할 수 있는 것을 알 수 있습니다.

Dagger 의 진입 장벽이 높지만 DI 을 통하여 개발한다면 유닛테스트 전환 및 유지보수에 도움이 될 것으로 판단됩니다. 하지만 진입 장벽이 높은 만큼 활용 범위는 고민해야 할 것으로 생각됩니다.

사용한 예제에 대한 소스코드를 참고하여 직접 작성해보시면 Android Dagger를 이해하는데 도움이 될 것으로 예상됩니다.

끝으로 Dagger을 이해하는데 잘 정리한 포스트를 제공해준 개발자 분들에게 감사의 뜻을 전합니다.


참고

Multi Module 과 Dagger2

DI, Dagger2 란?

DI(Dependency Injection) 와 Dagger2

DI 기본개념부터 사용법까지, Dagger2 시작하기

Dagger2 학습에 필요한 참고자료

Dagger Open Source

About

Android 에서 Dagger 사용 법을 정리했습니다.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published