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

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 질문이 아닙니다.



답변

당신의 질문에 관해서는

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자체 범위 종속성에 액세스하려면를 제공해야합니다 .

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


답변