Dagger Q&A


Singletons in Dagger

Q: Dagger 사용 중, 주입할 때 마다 싱글톤의 인스턴스가 여러개로 얻어지는 것을 발견했다. 나는 클래스와 프로바이드 메소드를 @Singleton으로 어노테이트했었다. 왜 이 현상이 일어나는지를 알고 있는 사람이 있나?

나는 Dagger GitHub의 샘플 어플리케이션와 동일한 구조를 따랐다. 나는 base activity에서 Singleton을 얻으려고 했고 customApplication 크래스에서 몇가지 3rd party 클래스들을 @Provides를 이용해서 제공받았다. 이것은 내가 각 액티비티에서 모듈을 원래의 object graph에 plus해서 그런가?

A: Dagger에서 @Singleton은 당신이 생각하는 것과 다르게 동작한다. JSR-330 스팩은 @Singleton javadoc에서 "그래프당 하나"라고 정의하며 Dagger는 이 정의대로 해석한다.

당신의 어플리케이션-그래프(더 짧은 수명의 그래프가 아니라)에서 실체화된 것을 @Singleton으로 마크하면 어플리케이션 당 하나의 인스턴스를 얻을 수 있다.

액티비티-그래프(plus() 오퍼레이션에서 사용된 모듈에 정의된 그래프로 얻어진 것)를 설정할 때 사용한 모듈안에 있는 특정 아이템을 @Singleton으로 어노테이트하면 이것은 액티비티-그래프 당 하나씩 얻을 수 있다.

만약 어플리케이션 당 하나를 원하면 어플리케이션-그래프에서 생성 되는 것을 확실하게 할 필요가 있다. 그것을 위해서는 두 방법 중 하나를 사용할 수 있다. 어플리케이션 모듈(들)에 @Provides 메소드를 명시적으로 제공하거나 또는 어플리케이션 모듈의 @Module(injects=..)에 있는 클래스들 중 하나로 기입한다.

(만약 @Singleton으로 마크하지 않았으면 주입 위치(injection site)당 하나씩 얻게 될 것이다.)

plus()로 생성된 그래프는 그것을 산출한 그래프와는 분리된 것으로 간주해야 하는 것을 기역하자. 그것을 감싸고 있고 그 내부에서 인스턴스들에 접근할 수 있지만 동일한 그래프는 아니다. (So remember, the graph created by plus() is seen as a separate graph that points to the graph from which it was spawned, and wraps it, can access instances within it, but is not the same graph.)

Note — 우리는 @Singleton의 현재 용법이 기술적으로 정확하고 스팩을 정확히 따르고 있긴하지만 용어의 명백하고 일반적인 의미에서 오해하게 만드는 것을 줄일 수 있도록 개선할 계획이 있다.

Comment

Q: 생성자가 부모 그래프의 여러 서브그래프에서 사용된 싱글톤은 어떻게 적용되는가? 부모 그래프에서 이미 존재하는 경우를 제외하면 그래프를 사용한 클래스당 하나씩 인가?

A: 어느 정도는 그렇다. 만약 모든 것이 "요청 즉시" 바인드되면 정확하게 그렇다. 만약 Singleton으로 마크된 것을 사용하면 그것을 처음 요청한 그래프에서 만들어 질 것이다. 그것을 부모 그래프(또는 루트 그래프)에서 생성되기를 원하면 @Provides 메소드 안에서 주입하거나, longer-lived 그래프 내부의 모듈 어노테이션의 injects= 속성안에 선언함으로서 그것이 longer-lived 그래프 안에 있다는 것을 Dagger가 알게 할 필요가 있다.


Q: Dagger module들 ‘injects’ 지시없이 사용할 수 있는가?

A: library에 대한 docs의 내용은 다음과 같다:
현재 모듈의 그래프에서 사용되지 않는 바인딩을 제공한다면 library = true를 선언하여야 한다.

하지만 Dagger가 주입 위치를 반드시 알아야 한다는 점은 library로 선언된 module로도 해결할 수 없다. object graph에는 반드시 injects가 나열된 module이 필요하다.

다음은 극히 단순화된 예제이다.

repo/
+- library/
| +- Foo.java
| `- FooModule.java
|
`- app/
+- BarActivity.java
`- BarModule.java

FooModule.java:

@Module(library = true)
public final class FooModule {
@Provides @Singleton provideFoo() {
return Foo();
}
}

BarModule.java:

@Module(
injects = BarActivity.class,
includes = FooModule.class
)
public final class BarModule {
}

In BarActivity.java (or similar):

ObjectGraph og = ObjectGraph.create(new BarModule());
og.inject(this);