dagger
Dagger2 Singleton
android by kotlin
2022. 12. 3. 22:10
① Member-Inject Method 또는 Provision Method 를 호출하면 Dagger 에 의해서 주입되거나 반환될 인스턴스가 매번 새로 생성됩니다. 만약 인스턴스를 매번 생성하지 않고 한번 생성된 인스턴스를 특정 스코프 내에서 계속해서 사용해야 하는 경우에는 @Singleton 어노테이션을 사용해야 합니다.
② @Provides 메서드에 의해 제공되는 인스턴스를 싱글톤 패턴으로 만들기 위해서는 @Provides 어노테이션에 추가로 @Singleton 어노테이션을 붙인 후 해당 모듈이 연결된 Component 에도 @Singleton 어노테이션을 붙여야 합니다.
③ 하나의 ComponentImpl 인스턴스를 통해 싱글톤 인스턴스를 요청할 때는 항상 동일한 인스턴스를 반환해주지만, 같은 ComponentImpl 클래스이지만 다른 인스턴스인 경우에는 동일한 인스턴스를 반환하지 않습니다. 즉 동일한 Component 인스턴스 내에서만 동일한 싱글톤 인스턴스가 반환됩니다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component1 = DaggerAppComponent.create()
val component2 = DaggerAppComponent.create(0
//instance1 과 instance2 는 동일한 인스턴스이지만
//instance3은 instance1, instance2 와는 다른 새로운 인스턴스입니다.
val instance1 = component1.getInstance()
val instance2 = component2.getInstance()
val instance3 = component3.getInstance()
}
④ Dagger 코드 분석
ComponentImpl 클래스
private static final class SingletonComponentImpl implements SingletonComponent {
private final SingletonComponentImpl singletonComponentImpl = this;
private Provider<Instance> provideInstanceProvider;
private SingletonComponentImpl(SingletonModule singletonModuleParam) {
initialize(singletonModuleParam);
}
/*
* ComponentImpl 의 필드에 DoubleCheck(Provider 구현 클래스) 인스턴스의 참조를 저장합니다.
* DoubleCheck 인스턴스의 필드에는 Component 의 요청에 의해 @Provides 메서드를 호출하는 Factory 클래스의 인스턴스의 참조를 저장하고 있습니다.
* */
@SuppressWarnings("unchecked")
private void initialize(final SingletonModule singletonModuleParam) {
this.provideInstanceProvider = DoubleCheck.provider(SingletonModule_ProvideInstanceFactory.create(singletonModuleParam));
}
@Override
public void inject(MainActivity mainActivity) {
injectMainActivity(mainActivity);
}
/*
* initialize 메서드에서 저장한 DoubleCheck 인스턴스의 참조를 통해 필드 주입에 필요한 인스턴스를 가져온 후 필드 주입을 합니다.
* */
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectInstance(instance, provideInstanceProvider.get());
return instance;
}
}
DoubleCheck.provider 메서드
public static <P extends Provider<T>, T> Provider<T> provider(P delegate) {
checkNotNull(delegate);
/*
delegate 인스턴스가 DoubleCheck 타입인지 확인합니다.
매개변수로 전달된 delegate 인스턴스는 Provider 인터페이스를 구현한 Factory 클래스입니다.
*/
if (delegate instanceof DoubleCheck) {
/* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
* binding, we shouldn't cache the value again. */
return delegate;
}
/*
delegate 인스턴스가 DoubleCheck 타입이 아닌 경우에는
DoubleCheck 타입으로 래핑합니다.
*/
return new DoubleCheck<T>(delegate);
}
"모듈명_메서드명Factory" 클래스
public final class SingletonModule_ProvideInstanceFactory implements Factory<Instance> {
private final SingletonModule module;
public SingletonModule_ProvideInstanceFactory(SingletonModule module) {
this.module = module;
}
@Override
public Instance get() {
return provideInstance(module);
}
/*
DoubleCheck.provider 메서드의 매개변수로 전달되는 인스턴스를 생성하는 static 메서드입니다.
내부에 Module 을 가지고 있습니다.
*/
public static SingletonModule_ProvideInstanceFactory create(SingletonModule module) {
return new SingletonModule_ProvideInstanceFactory(module);
}
public static Instance provideInstance(SingletonModule instance) {
return Preconditions.checkNotNullFromProvides(instance.provideInstance());
}
}
DoubleCheck get 메서드
/*
외부 클래스(예: MainActivity)에 의해 의존성 주입 요청이 들어오면
주입할 인스턴스를 받아오기 위해 ComponentImpl 의 필드에 저장되어 있는
Provider 타입의 인스턴스의 get 메서드를 호출합니다.
이 메서드는 동일한 인스턴스를 여러번 요청하더라도 동일한 인스턴스를 반환하도록 구현되어 있습니다.
kotlin 의 by lazy 를 사용했을 때 생성되는 코드의 형태와 유사합니다.
*/
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
instance = reentrantCheck(instance, result);
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
⑤요약 → Dagger 에 의해 자동으로 주입되는 인스턴스를 싱글톤 패턴으로 생성하기 위해서는 @Singleton 어노테이션을 사용하면 됩니다.