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.