Dagger 2에서 구성 요소 (객체 그래프)의 수명주기를 결정하는 요소는 무엇입니까?


134

Dagger 2의 스코프, 특히 스코프 그래프의 수명주기를 둘러 보려고합니다. 범위를 벗어날 때 정리할 구성 요소를 어떻게 작성합니까?

Android 애플리케이션의 경우 Dagger 1.x를 사용하는 경우 일반적으로 활동 레벨에서 하위 범위를 작성하도록 확장 할 애플리케이션 레벨에 루트 범위가 있습니다.

public class MyActivity {

    private ObjectGraph mGraph;

    public void onCreate() {
        mGraph = ((MyApp) getApplicationContext())
            .getObjectGraph()
            .plus(new ActivityModule())
            .inject(this);
    }

    public void onDestroy() {
        mGraph = null;
    }
}

하위 범위는 참조를 유지하는 한 존재했으며,이 경우 활동의 수명주기였습니다. onDestroy에서 참조를 삭제하면 범위가 지정된 그래프를 가비지 수집 할 수 있습니다.

편집하다

Jesse Wilson은 최근 mea culpa를 게시했습니다.

Dagger 1.0은 범위 이름을 잘못 정돈했습니다 ... @Singleton 주석은 루트 그래프와 사용자 정의 그래프 모두에 사용되므로 사물의 실제 범위가 무엇인지 알아내는 것이 까다 롭습니다.

그리고 내가 읽은 모든 것은 Dagger 2에 대한 요점이 스코프 작동 방식을 개선하지만 그 차이점을 이해하기 위해 고심하고 있습니다. 아래의 @Kirill Boyarshinov의 의견에 따르면 구성 요소의 수명주기 또는 종속성은 평소와 같이 구체적인 참조로 결정됩니다. 그렇다면 Dagger 1.x와 2.0 스코프의 차이점은 순전히 의미 명확성의 문제입니까?

내 이해

단검 1.x

의존성은 어느 쪽이든 아니든간에 요 @Singleton. 루트 그래프와 하위 그래프의 종속성에 대해서도 동일하게 적용되므로 종속성이 바인딩 된 그래프에 대한 모호함이 발생합니다 ( 단어에서 하위 그래프 내에 단일 톤이 캐시되어 있거나 새 활동 하위 그래프 일 때 항상 다시 작성 됨 참조). 건설 되었습니까? )

단검 2.0

사용자 지정 범위를 사용하면 의미 적으로 명확한 범위를 만들 수 있지만 기능적 @Singleton으로 Dagger 1.x 에 적용하는 것과 같습니다 .

// Application level
@Singleton
@Component( modules = MyAppModule.class )
public interface MyAppComponent {
    void inject(Application app);
}

@Module
public class MyAppModule {

    @Singleton @Named("SingletonScope") @Provides
    StringBuilder provideStringBuilderSingletonScope() {
        return new StringBuilder("App");
    }
}

// Our custom scope
@Scope public @interface PerActivity {}

// Activity level
@PerActivty
@Component(
    dependencies = MyAppComponent.class,
    modules = MyActivityModule.class
)
public interface MyActivityComponent {
    void inject(Activity activity);
}

@Module
public class MyActivityModule {

    @PerActivity @Named("ActivityScope") @Provides
    StringBuilder provideStringBuilderActivityScope() {
        return new StringBuilder("Activity");
    }

    @Name("Unscoped") @Provides
    StringBuilder provideStringBuilderUnscoped() {
        return new StringBuilder("Unscoped");
    }
}

// Finally, a sample Activity which gets injected
public class MyActivity {

    private MyActivityComponent component;

    @Inject @Named("AppScope")
    StringBuilder appScope

    @Inject @Named("ActivityScope")
    StringBuilder activityScope1

    @Inject @Named("ActivityScope")
    StringBuilder activityScope2

    @Inject @Named("Unscoped")
    StringBuilder unscoped1

    @Inject @Named("Unscoped")
    StringBuilder unscoped2

    public void onCreate() {
        component = Dagger_MyActivityComponent.builder()
            .myApplicationComponent(App.getComponent())
            .build()
            .inject(this);

        appScope.append(" > Activity")
        appScope.build() // output matches "App (> Activity)+" 

        activityScope1.append("123")
        activityScope1.build() // output: "Activity123"

        activityScope2.append("456")
        activityScope1.build() // output: "Activity123456"

        unscoped1.append("123")
        unscoped1.build() // output: "Unscoped123"

        unscoped2.append("456")
        unscoped2.build() // output: "Unscoped456"

    }

    public void onDestroy() {
        component = null;
    }

}

사용 @PerActivity하는 것이 당신의 의도 를 전달 한다는 것 하면이 구성 요소의 수명주기에 대한 있지만 궁극적으로 언제 어디서나 구성 요소를 사용할 수 있습니다. Dagger의 유일한 약속은 주어진 컴포넌트에 대해 범위 어노테이션이있는 메소드가 단일 인스턴스를 리턴한다는 것입니다. 또한 Dagger 2가 구성 요소의 범위 주석을 사용하여 모듈이 범위가 같거나 범위가 아닌 종속성 만 제공하는지 확인합니다.

요약해서 말하자면

종속성은 여전히 ​​싱글 톤 또는 비 싱글 톤이지만 @Singleton이제 응용 프로그램 수준의 싱글 톤 인스턴스를위한 것이며 사용자 지정 범위는 수명주기가 짧은 싱글 톤 종속성에 주석을 추가하는 데 선호되는 방법입니다.

개발자는 더 이상 필요하지 않은 참조를 삭제하여 구성 요소 / 종속성의 수명주기를 관리하고 구성 요소가 의도 한 범위 내에서 한 번만 생성되도록해야하지만 사용자 지정 범위 주석을 사용하면 해당 범위를 쉽게 식별 할 수 있습니다. .

$ 64k 질문 *

Dagger 2 범위 및 라이프 사이클에 대한 이해가 정확합니까?

* 실제로 $ 64'000 질문이 아닙니다.


5
당신은 아무것도 그리워했다. 각 구성 요소의 라이브 사이클 관리는 수동입니다. 내 경험으로는 Dagger 1에서도 마찬가지였습니다. plus()새 그래프에 대한 참조를 사용하여 Application 레벨 ObjectGraph 오브젝트를 서브 그래프 할 때 활동에 저장되었으며 라이브 사이클에 바인드되었습니다 (에서 참조 됨 onDestroy). 범위는 컴파일 타임에 모든 종속성을 충족하면서 오류없이 구성 요소 구현이 생성되도록합니다. 따라서 문서화 목적만을위한 것이 아닙니다. 이 스레드 에서 몇 가지 예를 확인하십시오 .
Kirill Boyarshinov

1
이것을 명확하게하기 위해 "범위가 지정되지 않은"제공자 메소드는 모든 주입에서 새 인스턴스를 리턴합니까?
user1923613

2
component = null을 설정하는 이유는 무엇입니까? onDestroy ()에서?
Marian Paździoch

답변:


70

당신의 질문에 관해서는

Dagger 2에서 구성 요소 (객체 그래프)의 수명주기를 결정하는 요소는 무엇입니까?

짧은 대답은 당신이 그것을 결정하는 것 입니다. 구성 요소에는 다음과 같은 범위가 주어질 수 있습니다.

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

다음 두 가지에 유용합니다.

  • 범위의 유효성 검사 : 구성 요소에는 범위가 지정되지 않은 공급자 또는 구성 요소와 범위가 같은 범위의 공급자 만있을 수 있습니다.

.

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Module
public class ApplicationModule {
    @ApplicationScope //application-scoped provider, only one can exist per component
    @Provides
    public Something something() {
         return new Something();
    }

    @Provides //unscoped, each INJECT call creates a new instance
    public AnotherThing anotherThing() {
        return new AnotherThing();
    }
}
  • 범위가 지정된 종속성의 하위 범위를 지정할 수 있으므로 "superscoped"구성 요소에서 제공된 인스턴스를 사용하는 "subscoped"구성 요소를 만들 수 있습니다.

@Subcomponent주석 또는 구성 요소 종속성 으로 수행 할 수 있습니다 . 나는 개인적으로 의존성을 선호합니다.

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);

    ActivityComponent newActivityComponent(ActivityModule activityModule); //subcomponent factory method
}

@Subcomponent(modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = applicationComponent.newActivityComponent(new ActivityModule(SomeActivity.this));

또는 구성 요소 종속성을 사용할 수 있습니다

@Component(modules={ApplicationModule.class})
@ApplicationScope
public class ApplicationComponent {
    Something something(); 
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Component(dependencies={ApplicationComponent.class}, modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent extends ApplicationComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule(SomeActivity.this)).build();

알아야 할 중요한 사항 :

  • 범위가 지정된 공급자 는 각 구성 요소에 대해 해당 범위 에 대해 하나의 인스턴스를 만듭니다 . 구성 요소는 자체 인스턴스를 추적하지만 다른 구성 요소에는 공유 범위 풀이나 마법이 없습니다. 주어진 범위에서 하나의 인스턴스를 가지려면 하나의 구성 요소 인스턴스가 필요합니다. 그렇기 때문에 ApplicationComponent자체 범위 종속성에 액세스하려면를 제공해야합니다 .

  • 구성 요소는 하나의 범위가 지정된 구성 요소 만 범위를 지정할 수 있습니다. 여러 범위의 구성 요소 종속성이 허용되지 않습니다.


구성 요소는 하나의 범위가 지정된 구성 요소 만 범위를 지정할 수 있습니다. 여러 범위의 구성 요소 종속성은 허용되지 않습니다 (버그가 있다고 생각하지만 모두 다른 범위를 가지고 있더라도). 무슨 뜻인지 이해하지 못합니다
Damon Yuan

그러나 라이브 사이클은 어떻습니까? 활동이 파괴 된 경우 ActivityComponent가 가비지 수집기를 후보로합니까?
Sever

다른 곳에 저장하지 않으면 예
EpicPandaForce

1
따라서 컴포넌트를 통해 주입 된 객체가 Activity를 통해 라이브로 작동하면 Activity 내부에 컴포넌트를 빌드합니다. 조각을 통해서만 생존하려면 조각 안에 구성 요소를 만들어야합니다. 구성 요소 인스턴스를 유지하는 위치는 어디입니까?
Thracian

특정 활동을 통해 생존하기 위해서는 어떻게해야합니까?
Thracian
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.