예제 링크 : https://github.com/seyoungcho2/Dagger2Example
이 글은 예제7,8,9과 함께 한다.
Scope란?
Dagger2의 역할은 외부에서 의존성을 주입해주는 것이다. 여기서 문제점이 생긴다. 외부(Container)에서 제공하는 인스턴스들이 한 번만 생성되어도 되는데 호출할 때마다 생성된다면 불필요한 메모리 사용이 생긴다.
Dagger2에서는 이를 해결하기 위해 Scope Annotation을 이용해 같은 범위(Scope)의 내에서 객체들을 관리하게 된다. Scope Annotation이 적용된 Provider은 범위(Scope) 내에서 한 번만 객체를 생성한다.
Dagger에서는 Scope Annotation을 이용해 한 번 생성된 객체가 재사용되는 범위(Scope)를 관리한다.
예를 들어 다음과 같은 Application class가 있다고 해보자
class Application()
Scope 적용이 안되었을 경우(예제7)
이러한 Application을 제공하는 Component와 Module을 다음과 같이 설정한다.
@Component(modules = [AppModule::class])
interface AppComponent {
fun getApplication() : Application
}
@Module
class AppModule {
@Provides
fun providesApplication() = Application()
}
이렇게 할 경우 이 Component로부터 getApplication()을 이용해 두 개의 Application을 받아왔을 때, 두 Application 에 대한 동일성 연산(===)은 false가 나온다. getApplication을 할 때마다 Application이 생성되기 때문이다.
fun main() {
val appComponent: AppComponent = DaggerAppComponent.create()
val application1 = appComponent.getApplication()
val application2 = appComponent.getApplication()
println(application1 === application2) // false
}
Scope 적용이 되었을 경우(예제8)
Component와 Provider에 다음과 같이 Singleton Scope를 적용한다.
*여기서의 Singleton Scope는 객체가 재사용되는 범위를 설정하기 위한 것이다. Process가 살아있는 동안 하나의 인스턴스만 생성한다는 원래의 Singleton 개념이 아니다. 다음 예제에서 자세히 다룬다.
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
fun getApplication() : Application
}
@Module
class AppModule {
@Singleton
@Provides
fun providesApplication() = Application()
}
이렇게 할 경우 이 Component로부터 두 개의 Application에 대한 동일성 연산(===)은 true가 나온다.
fun main() {
val appComponent: AppComponent = DaggerAppComponent.create()
val application1 = appComponent.getApplication()
val application2 = appComponent.getApplication()
println(application1 === application2) // true
}
Singleton Scope는 Singleton을 위한 것이 아니다.
위에 나오는 Singleton Scope는 이름 때문에 많은 사람들이 헷갈려한다. 하지만, Singleton Scope는 Singleton 객체를 만들기 위한 것이 아니라 단순히 @Scope Annotation이 적용된 Annotation Processor일 뿐이다.
먼저 내부를 보면 Singleton Scope는 다음과 같이 설정되어 있다.
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
이러한 Singleton 대신 다른 이름의 Scope를 만들어 쓴다면 어떨까? 아래에서 CustomScope를 만들어 테스트한다.
CustomScope만들기(예제9)
먼저 위 Singleton annotation과 같은 역할을 하는 Kotlin annotation class를 만든다.
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class CustomScope()
자 이제 AppComponent, AppModule에 우리가 만든 CustomScope를 적용한다.
@CustomScope
@Component(modules = [AppModule::class])
interface AppComponent {
fun getApplication() : Application
}
@Module
class AppModule {
@CustomScope
@Provides
fun providesApplication() = Application()
}
자 이제 이 Scope동안은 하나의 Application만이 생성된다. 따라서 아래의 코드가 돌아갈 경우 동등성 연산에서 true를 반환한다.
fun main() {
val appComponent: AppComponent = DaggerAppComponent.create()
val application1 = appComponent.getApplication()
val application2 = appComponent.getApplication()
println(application1 === application2)// true
}
Scope는 객체가 재사용되는 범위만을 가리킬 뿐이다.