본 포스팅은 DroidKaigi 2019 ~ マルチモジュールプロジェクトでの Dagger2を用いた Dependency Injection 을 기본으로 번역하여 작성했습니다
제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.
일부 이미지는 이해를 돕고자 한국어로 수정했습니다.
번역에서 사용한 이미지는 원본은 https://speakerdeck.com/kgmyshin/android-multi-module-with-dagger 에 있습니다.
Multi Module로 구성된 프로젝트에서 Dagger2를 사용해 Dependency Injection을 할 때의 고민 포인트를 알아보고 그 해결책을 이해한다.
멀티 모듈 프로젝트에서의 Dagger를 사용해 Dependency Injection
@kgmyshin DroidKaigi 2019
이 세션의 목적지
멀티 모듈로 구성된 프로젝트에서 Dagger2를 사용해 Dependency Injection을 할 때의 고민 포인트를 알아보고 그 해결책을 이해한다.
agenda
혹시 세션을 듣는 도중에 모르는 부분이 있다면, 꼭 DMM.com 부스로 와주세요!
Dependency Injection이란
한마디로, “의존 객체”를 “주입”하는 디자인 패턴입니다
internal class AllergenRepositoryImpl : AllergenRepository {
private val apiClient = ApiClient()
override fun findAll(
): Single<List<Allergen>> =
apiClient.getAllergens()
override fun findById(
allergenId: AllergenId
): Maybe<Allergen> =
apiClient.getAllergen(allergenId.value)
}
DI가 없다
internal class AllergenRepositoryImpl @Inject constructor(
private val apiClient: ApiClient
) : AllergenRepository {
override fun findAll(
): Single<List<Allergen>> =
apiClient.getAllergens()
override fun findById(
allergenId: AllergenId
): Maybe<Allergen> =
apiClient.getAllergen(allergenId.value)
}
DI 적용
장점
이외의 의존도를 구상 항목에 가두는 것으로 손쉽게 라이브러리를 교체할 수 있다
Day2 14:50~ 지금부터 시작하는 의존성 주입 by kobakei-san
Dagger2 재입문
앱에서 DI를 하기 위해서는?
어디에 무엇을
Inject할지를 망라할 필요가 있다
Dagger2를 사용한 구현의 흐름
Module을 준비
※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다
Module의 작성법 사례 : 타입과 인스턴스를 연결한다
Dagger2를 사용한 구현의 흐름
Component를 준비
※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다
Component에는 “무엇을 제공할까” 이외를 적는다
=> “어디에 제공할 것인가”를 주로 적는다
Component 작성법의 예 : inject 하는 곳을 파라미터로 하는 함수를 정의
@Singleton
@Component(modules = [GreetingModule::class])
interface GreetingComponent {
fun inject(activity: GreetingActivity) // GreetingActivity에 inject
fun inject(fragment: GreetingFragment) // GreetingFragment에 inject
fun greeter(): Greeter // 어디든지!
}
Dagger2를 사용한 구현의 흐름
Application#onCreate로 Component를 초기화
※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다
class Application : android.app.Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.create() // 초기화
}
}
Dagger2를 사용한 구현의 흐름
각 위치에서 inject
※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다
Dagger2를 사용한 구현의 흐름
각 위치에서 inject
※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다
Dagger-Android 재입문
Dagger2를 사용한 구현의 흐름
dagger.android
사용한 구현의 흐름
Module을 준비
@Module
abstract class AppModule {
@Binds
abstract fun messageProvider(impl: MessageProviderImpl): MessageProvider
}
dagger.android 사용한 구현의 흐름
FragmentModule을 준비
@Module
abstract class MainFragmentModule {
@ContributesAndroidInjector
abstract fun mainFragment(): MainFragment
}
dagger.android 사용한 구현의 흐름
Component를 준비
@Singleton
@Component(
modules = [
AndroidInjectionModule::class, // 추가
AppModule::class,
MainFragmentModule::class // 추가
]
)
interface AppComponent : AndroidInjector<Application>
dagger.android 사용한 구현의 흐름
Application#onCreate로 Component를 초기화
dagger.android 사용한 구현의 흐름
각 위치에서 inject
dagger.android 사용한 구현의 흐름
각 위치에서 inject
MainFragment에서 Inject 요청 시 먼저 부모 Activity에서 HasSupportFragmentInjector를 구현했는지 확인한 후, Application으로 이동해서 확인한다. 실제 Injector 구현체에 도달하는 흐름이다.
멀티 모듈에서 Dependency Injection의 어려움
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
Application#onCreate로 Component를 초기화
각각의 Component Inject가 호출한다
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
각 위치에서 inject
Dagger2를 사용해 멀티 모듈 프로젝트로 DI 구현하기
Application에 Fragment용 Injector를 하나밖에 둘 수 없으므로, Injector가 덮어쓰게된다
Application에서 가지는 하나의 FragmentInjector가 복수의 Injector를 가지도록 한다
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
Module을 준비
@Module
internal abstract class CounterModule {
@Singleton
@Binds
abstract fun provideCounter(impl: CounterImpl): Counter
}
@Module
internal abstract class GreetingModule {
@Binds
abstract fun provideGreeter(impl: GreeterImpl): Greeter
}
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
FragmentModule을 준비
@Module
internal abstract class GreetingFragmentModule {
@ContributesAndroidInjector
abstract fun greetingFragment(): GreetingFragment
}
@Module
internal abstract class CounterFragmentModule {
@ContributesAndroidInjector
abstract fun counterFragment(): CounterFragment
}
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
Injector를 준비
class CounterInjector : HasDispatchingInjector<Fragment> {
@Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun dispatchingAndroidInjector(): DispatchingAndroidInjector<Fragment> =
supportFragmentInjector
}
class GreetingInjector : HasDispatchingInjector<Fragment> {
@Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun dispatchingAndroidInjector(): DispatchingAndroidInjector<Fragment> =
supportFragmentInjector
}
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
Component를 준비
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
CounterModule::class,
CounterFragmentModule::class])
interface CounterComponent : AndroidInjector<CounterInjector>
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
GreetingModule::class,
GreetingFragmentModule::class])
interface GreetingComponent : AndroidInjector<GreetingInjector>
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
Application#onCreate로 Component를 초기화
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
각 위치에서 inject
class CounterFragment : Fragment() {
@Inject
lateinit var counter: Counter
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
...
}
class GreetingFragment : Fragment() {
@Inject
lateinit var greeter: Greeter
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
...
}
Dynamic Feature Module
지금까지와 같은 순서로 dagger.android를 사용해 구현해본다
Application#onCreate로 Component를 초기화
← X 불가능Activity에 moduleInjector가 있는 경우를 고려해서 AndroidSupportInjection과 같은 구현을 할 수도 있다
※ ActivityInjector를 가질 때에는 이 방법은 잘 안되므로 조건 분기할 필요가 있다!
들어주셔서 감사합니다.
comments powered by Disqus
Subscribe to this blog via RSS.