brunch

You can make anything
by writing

C.S.Lewis

by 이승현 May 22. 2017

Dagger 2 #03

Dagger 2

Dagger 2


이전 글을 통해 Dagger를 이용한 의존성 주입(Dependency Injection) 구현법과 주요 Annotation 들을 간단히 살펴봤습니다.

@Inject

@Provides, @Module

@Component



이번에는 다른 Annotation들과 기타 Injections들을 살펴보겠습니다.

@Singleton

@Reusable

@CanReleaseReferences

@Qualifier


Lazy injections

Provider injections




Dagger scope


먼저 Dagger에서 쓰이는 Scope의 개념부터 알아야 합니다.


Java에서 변수들은 사용 가능한 범위를 가지고, 그 범위를 변수의 Scope라고 합니다.

// Scope
public class ScopeClass {

    int globalScopeVariables = 0;

    void temp() {
        int localScopeVariables = 1;
    }

}


Dagger에서는 조금 의미가 다릅니다.


Dagger의 Scope는
해당 클래스의 단일(Single) 인스턴스가 존재하는 범위를 말합니다.


여기서 말하는 존재는 Garbage-collected가 발생할 때까지 Component 객체가 바인딩된 단일 인스턴스에 대한 참조를 보유한다는 의미입니다. 흔히 알고 있는 하나의 인스턴스만 만들어서 이용하는 싱글톤 패턴과 비슷한데,  Application 전체가 아닌 범위도 지정할 수 있습니다.

예를 들면, Application scope는 Application의 생명주기만큼 오래 존재하고, Activity scope는 Activity가 존재하는 한 계속 존재할 수 있다는 의미입니다.


#01 Dagger scope




#01 Singleton scope


Application scope를 가지는 javax.inject.Singleton annotation입니다.

@Provides와 @Component에 @Singleton을 추가하면 됩니다.

Application Scope이기 때문에 Applicatino이 존재하는 한, 클래스의 단일 인스턴스도 존재하게 됩니다.


아래 코드는 동일한 인스턴스(의존성)를 주입하기 위해 @Provides와 @Component에 @Singleton을 추가한 코드입니다.

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

@Module
public class MediaModule {

    @Provides @Singleton
    public Video provideVideo() {
        return new Video();
    }

}
import javax.inject.Singleton;

import dagger.Component;

@Singleton
@Component(modules = {MediaModule.class})
public interface MediaComponent {

    MediaController mediaController();

}


여기서 주의할 점이 있습니다.

Dagger 2 does not allow unscoped components to use modules with scoped bindings.
Unscoped components cannot have scoped dependencies.


Dagger 2에서는 서로 다른 Scope로 인해 문제가 일어날 수 있기 때문에 unscope Component는 scope Module을 가질 수 없습니다.

즉, Application scope인 @Singleton @Provides을 이용하기 위해서는 @Singleton @Component를 이용해야 합니다.




#02 Reusable scope


바인딩된 객체가 반환될 때 재사용될 수도 아닐 수도 있는 scope를 가지는 dagger.Reusable annotation입니다.

정의가 애매한데 @Singleton과 비슷합니다.


@Singleton

항상 동일한 인스턴스를 재사용.

@Component와 관련 있음.


@Reusable

새로운 인스턴스를 생성할 수 있지만, 이미 인스턴스가 존재한다면 재사용.

다른 scope와 달리 @Component와 관련 없음.


항상 동일한 인스턴스를 이용해야 하는 환경이 아니라면 메모리 할당 측면에서 더 유용합니다.


import dagger.Module;
import dagger.Provides;
import dagger.Reusable;

@Module
public class MediaModule {

    // 각 @Component 별로 인스턴스를 새로 생성할 수 있기 때문에, immutable 객체에만 이용
    @Provides @Reusable
    public Video provideVideo() {
        return new Video();
    }

}





#03 Releasable references


GC가 발생했을 때, dagger.CanReleaseReferences annotation을 이용하여 해당 인스턴스를 해제할 수 있습니다.


안드로이드와 같이 메모리가 중요한 환경에서는 메모리 부족 현상을 겪고 있을 때, GC가 발생하면 현재 이용되지 않는 범위가 지정된 객체를 삭제할 수 있습니다.

Java의 WeakReference와 비슷한 개념입니다.


자세한 내용은 아래 링크를 참조하시기 바랍니다.




#04 Qualifiers


때로는 타입만으로 의존성을 식별하기 어려운 경우, javax.inject.Qualifier annotation이용할 수 있습니다.


1. 우선 @Qualifier annotataion을 생성합니다.

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Qualifier;

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface VideoType {
    String value() default "";
}


2. 의존성을 식별하기 위해 생성한 @Qualifier annotataion(@VideoType)을 추가합니다.

import javax.inject.Inject;

public class MediaController {

    @Inject @VideoType("old") Video oldVideo;
    @Inject @VideoType("new") Video newVideo;

    @Inject MediaController() {}

    public String getCurrentTime() {
        int oldCurrentPosition = oldVideo.getCurrentPosition();
        int newCurrentPosition = newVideo.getCurrentPosition();
    }

}


3. 해당 @Provides 메서드에 @VideoType annotation을 추가해 규정된(qualified) 값을 제공합니다.

import dagger.Module;
import dagger.Provides;

@Module
public class VideoModule {

    @Provides @VideoType("old")
    public Video provideOldVideo() {
        return new Video();
    }

    @Provides @VideoType("new")
    public Video provideNewVideo() {
        return new Video();
    }

}




#05 Lazy injections


Dagger에서도 Lazy initialization 개념을 이용할 수 있습니다.

Lazy <T>를 이용해 객체가 생성될 때까지 초기화를 늦출 수 있습니다.


import javax.inject.Inject;

import dagger.Lazy;

public class MediaController {

    Lazy<Video> video;

    @Inject
    MediaController(Lazy<Video> video) {
        this.video = video;
    }

    public String getCurrentTime() {
        // Video created once on first call to .get() and cached.
        int currentPosition = video.get().getCurrentPosition();
        return Integer.toString(currentPosition);
    }

}




#06 Provider injections


Provider <T>를 이용하여 매번 새로운 객체를 초기화할 수도 있습니다.


import javax.inject.Inject;
import javax.inject.Provider;

public class MediaController {

    Provider<Video> video;

    @Inject
    MediaController(Provider<Video> video) {
        this.video = video;
    }

    public String getCurrentTime() {
        // new Video every time.
        int currentPosition = video.get().getCurrentPosition();
        return Integer.toString(currentPosition);
    }

}


Direct injection, Provider injection and Lazy injection 이 3가지의 차이점은 아래 링크를 참조해 주시기 바랍니다.





https://google.github.io/dagger/users-guide.html




이제 조금 이해가 가네요.

단순하게 Dependency 요청하고, 주입해주는 framework인데 이를 도와주는 몇 가지 annotation들이 있는 구조네요.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari