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 で参照を削除することで、スコープ グラフがガベージ コレクションの対象になるようになりました。
編集
ジェシー・ウィルソンは最近、申し訳ない
Dagger 1.0 ではスコープ名がひどく間違っていました... @Singleton アノテーションはルート グラフとカスタム グラフの両方に使用されるため、実際のスコープが何であるかを把握するのは困難です。
私が読んだり聞いたりした他のすべては、Dagger 2 ではスコープの動作方法が改善されていることを示していますが、違いを理解するのに苦労しています。以下の @Kirill Boyarshinov のコメントによると、コンポーネントまたは依存関係のライフサイクルは、通常どおり、具体的な参照によって決定されます。では、Dagger 1.x と 2.0 のスコープの違いは、純粋に意味の明確さの問題なのでしょうか?
私の理解
ダガー 1.x
依存関係はどちらか@Singleton
一方であった。これはルートグラフとサブグラフの依存関係にも当てはまり、依存関係がどのグラフにバインドされているかが曖昧になる(Dagger では、サブグラフ内のシングルトンはキャッシュされますか、それとも新しいアクティビティ サブグラフが構築されるときに常に再作成されますか?)
ダガー 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
現在はアプリケーション レベルのシングルトン インスタンスを対象としており、カスタム スコープは、ライフサイクルが短いシングルトン依存関係に注釈を付ける推奨される方法です。
開発者は、不要になった参照を削除してコンポーネント/依存関係のライフサイクルを管理し、コンポーネントが意図したスコープ内で 1 回だけ作成されるようにする責任がありますが、カスタム スコープ注釈を使用すると、そのスコープをより簡単に識別できます。
64,000 ドルの質問*
Dagger 2 のスコープとライフサイクルについての私の理解は正しいでしょうか?
* 実際には 64,000 ドルの価値のある質問ではありません。
ベストアンサー1
あなたの質問に関して
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();
}
}
- スコープ指定された依存関係をサブスコープ指定できるため、「スーパースコープ指定された」コンポーネントから提供されたインスタンスを使用する「サブスコープ指定された」コンポーネントを作成できます。
これは、アノテーションまたはコンポーネントの依存関係を使用して実行できます@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 つのコンポーネントのみをサブスコープできます。複数のスコープ指定されたコンポーネントの依存関係は許可されません。