1. ホーム
  2. java

[解決済み] Dagger 2のコンポーネント(オブジェクトグラフ)のライフサイクルを決定するものは何ですか?

2022-05-31 13:29:38

質問

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;
    }
}

子スコープが存在するのは、それへの参照を保持している間だけで、この場合はActivityのライフサイクルのことです。onDestroyで参照を削除することで、スコープされたグラフがガベージコレクトされるのを確実にします。

EDIT

Jesse Wilsonは最近 誤字脱字

<ブロッククオート

Dagger 1.0はスコープ名をひどく間違えていました ... Singletonアノテーションはルートグラフとカスタムグラフの両方に使用されるので、実際のスコープが何であるかを把握するのは厄介です。

と、私が読んだり聞いたりしたすべてのことは、Dagger 2がスコープの動作方法を改善することを指していますが、その違いを理解するのに苦労しています。下の @Kirill Boyarshinov のコメントによると、コンポーネントまたは依存関係のライフサイクルは、これまでどおり、具体的な参照によって決定されるとのことです。つまり、Dagger 1.x と 2.0 のスコープの違いは、純粋に意味上の明確さの問題なのでしょうか?

私の理解

ダガー1.x

依存関係は @Singleton であるか、そうでないかです。これはルートグラフとサブグラフの依存関係にも同様に当てはまり、依存関係がどのグラフに束縛されているのかが曖昧になる原因となっていました( Daggerでは、サブグラフ内のシングルトンはキャッシュされるのですか? )

Dagger 2.0

カスタムスコープを使用すると、意味的に明確なスコープを作成できますが、機能的には @Singleton を適用するのと機能的には同じです。

// 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 はアプリケーションレベルのシングルトンインスタンスを対象としており、カスタムスコープはライフサイクルが短いシングルトン依存関係をアノテートするための好ましい方法です。

開発者は、不要になった参照を削除してコンポーネント/依存関係のライフサイクルを管理し、コンポーネントが意図したスコープで一度だけ作成されることを保証する責任がありますが、カスタムスコープアノテーションによってそのスコープを特定しやすくなります。

6400 万ドルの質問*」。

Dagger 2のスコープとライフサイクルに関する私の理解は正しいですか?

* 実際には64'000ドルの質問ではありません。

どのように解決するのですか?

ご質問の件ですが

Dagger 2のコンポーネント(オブジェクトグラフ)のライフサイクルは何で決まるのでしょうか?

短い答えは あなたが決めることです。 . コンポーネントは以下のようにスコープを与えることができます。

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

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

これらは、2つの点で役に立ちます。

  • スコープの検証: コンポーネントは、スコープされていないプロバイダ、またはコンポーネントと同じスコープのプロバイダしか持つことができません。

.

@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();

大切なこと

  • スコープ付きプロバイダは、与えられたスコープに対して1つのインスタンスを作成します。 各コンポーネントに対して . つまり、コンポーネントは自分自身のインスタンスを追跡しますが、他のコンポーネントは共有スコーププールや何らかの魔法を持っていないことを意味します。あるスコープで1つのインスタンスを持つには、そのコンポーネントのインスタンスが1つ必要です。 そのために ApplicationComponent を使用して、自身のスコープされた依存関係にアクセスすることができます。

  • コンポーネントは、1つのスコープされたコンポーネントのみをサブスコープすることができます。複数のスコープされたコンポーネントの依存関係は認められません。