티스토리 뷰
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-androiddagger 에서 android Library 을 활용할 수 있는 라이브러리 입니다.
com.google.dagger:dagger-android-supportdagger 에서 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;
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}Application는 component , MainActivity 는 subcomponent 을 구성하게 됩니다. MainActivity에서는 inject 역할을 담당해줄 @Subcompoent 와 외존성을 설정하는 @Module 을 구성합니다.
MainModule.class
(subcomponents = MainFragmentComponent.class)
abstract class MainModule {
abstract MainActivityContract.View bindView(MainActivity activity);
public abstract MainActivityContract.Presenter bindPresenter(MainPresenterImpl mainPresenter);
}
ActivityScope.class
public @interface ActivityScope {
}
MainComponent.class
(modules = MainModule.class)
public interface MainComponent extends AndroidInjector<MainActivity> {
.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity>{
}
}@Subcomponent annotation 을 붙여주고 의존성 설정을 위해 MainModule 을 설정합니다.MainComponent.class 에서 주의 깊게 살펴볼 것은 AndroidInject 를 상속하는 것입니다. AndroidInject 을 상속받으면서 interface로 MainComponent 을 구성하고 @Subcomponent.Builder 작성해주면 @Subcomponent 작성이 끝납니다.
AndroidInject
AndroidInject 는 dagger.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 에서 Component 와 Module 을 구성하게 됩니다. 이유는 Application의 Lifecycle 에 따라 Component 는 Singleton 으로 관리 되어야하며 inject 또한 Application에서 이뤄지기 때문입니다. Module 안에 구성 또한 Application Lifecycle 을 적용하게 되어 Singleton으로 적용 됩니다.
AppComponent.class
(modules = {
AndroidInjectionModule.class,
AppModule.class
})
public interface AppComponent {
void inject(DaggerSampleApp daggerSampleApp);
}Singleton 을 Scope 로 구성하였으며 Application 에서 inject 발생하므로 members-injection 메서드 구성 하였습니다. 또한 AndroidInjectionModule.class 을 Modules 로 설정하면서 dagger.android 에서 제공하는 Class을 사용할 수 있습니다.
AppModule.class
(subcomponents = MainComponent.class)
abstract class AppModule {
abstract DataSource bindDataSource(DataSourceImpl dataSource);
(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindMainActivity(MainComponent.Builder builder);
}Android 에서 AndroidInjector 사용을 위해서는 AndroidInject.Factory Binds 을 추가로 만들어줘야 합니다. Activity 는 해당하는 @Subcomponent 를 통해 inject 되므로 Subcomponent.builder 를 파라미터로 받는 바인터를 만들어 줍니다.AndroidInjector.Factory 는 MultiBinds 동작이 되므로 @ActivityKey(dagger.android) 를 지원하고 있습니다. (다른 컴포넌트들에 대한 key annotation 도 지원합니다. 예시. FragmentKey)
Application, MainActivity Inject
DaggerSampleApp.class
public class DaggerSampleApp extends Application implements HasActivityInjector{
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
public void onCreate() {
super.onCreate();
// inject
DaggerAppComponent.create().inject(this);
}
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 {
MainActivityContract.Presenter presenter;
protected void onCreate( 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(생성자) 로 제공하고 있습니다. MainPresenterImpl 을 MainActivity 에 제공 하기 위해서 @Inject constructor(생성자) 을 사용하면 다음과 같습니다.
public class MainPresenterImpl implements MainActivityContract.Presenter {
MainActivityContract.View view;
DataSource dataSource;
public MainPresenterImpl(MainActivityContract.View view, DataSource dataSource){
this.view =view;
this.dataSource = dataSource;
}
}MainFragment DI 구현 방법
Fragment 는 Activity 안에서만 동작하므로 부모가 Activity인 @Subcomponent 을 구성하게 됩니다. Fragment 도 Lifecycle 을 갖고 있기에 @FragmentScope 을 설정하였습니다.
FragmentScope.class
public @interface FragmentScope {
}
MainFragmentComponent.class
(modules = MainFragmentModule.class)
public interface MainFragmentComponent extends AndroidInjector<MainFragment> {
.Builder
abstract class Builder extends AndroidInjector.Builder<MainFragment> {
}
}Fragment에서 AndroidInjector 로 inject를 수행하려면 부모가 되는 Activity에 Injector를 생성해야 합니다. 생성 방법은 MainActivity 에서 설정하였던 방식과 동일합니다.
Fragment 에서 AndroidInjector 을 사용하여 inject을 수행하려면 Fragement 을 사용할 Activity에 Injector를 생성해야 합니다. Activity에 HasSupportFragmentInjector 을 imprement 하고 DispatchingAndroidInjector 을 바인딩(Binding) 받아 리턴합니다.
MainActivity.class
public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector, MainActivityContract.View {
MainActivityContract.Presenter presenter;
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
public AndroidInjector<Fragment> supportFragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
//...
}Activity Module에 Subcomponent로 등록하고 AndroidInjector.Factory 에 바인딩 해줄 Component.Builder을 설정합니다. Activity에서도 언급했듯이 AndroidInjector.Factory 은 MultiBinding 을 하기에 FragmentKey로 사용하여 Key를 설정합니다.
MainModule.class
(subcomponents = MainFragmentComponent.class)
abstract class MainModule {
abstract MainActivityContract.View bindView(MainActivity activity);
public abstract MainActivityContract.Presenter bindPresenter(MainPresenterImpl mainPresenter);
(MainFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindMoviesFragment(MainFragmentComponent.Builder builder);
}@FragmentKey 입력 시 주의사항
FragmentKey는dagger.android.support와dagger.android두 패키지에 모두 있습니다. 따라서 FragmentKey로 사용하려는 Fragment가android.support.v4.app.Fragment와android.app.Fragment패키지 중 소속되어 있는지 확인 후 알맞은 패키지를import해야 합니다.
Fragment Component 에서 AndroidSupportInjector을 사용하였기에 Fragment 에서는 inject 을 호출만으로 바인딩 할 수 있습니다.
public class MainFragment extends Fragment implements MainFragmentContract.View{
MainFragmentContract.Presenter presenter;
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을 이해하는데 잘 정리한 포스트를 제공해준 개발자 분들에게 감사의 뜻을 전합니다.
참고
'프로그래밍 > Android' 카테고리의 다른 글
| Android Chuck Library 사용해보자 (0) | 2018.08.17 |
|---|---|
| Android RXJava 을 활용한 Variable Overable 만들기 (0) | 2018.07.26 |
| Dagger 간단히 알아보기 (0) | 2018.07.18 |
| Android Google Play 배지 받는 방법 (0) | 2018.07.13 |
| 형변환이란? (0) | 2018.07.10 |
- missionchina
- IT
- missioon
- 점수판
- 미션차이나센터
- 패턴
- Android
- 코틀린
- 스코어헬퍼
- RXjava
- 임용고시
- flutter
- view
- IOS
- 탁구
- DI
- MCC
- issue
- 선교
- 알고리즘
- 고시문헬퍼
- 안드로이드
- 고시문
- Kotlin
- 디자인패턴
- Android Studio
- java
- swift
- push
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |