每个片段的 Dagger 2 自定义范围(或 Activity 等...)
Dagger 2 Custom Scope for each Fragment (or Activity etc...)
我看过几篇不同的文章,它们似乎提出了在 Dagger 2 中进行自定义范围界定的两种不同方法:
MVP Presenters that Survive Configuration Changes Part-2 (Github repo):
- 为每个片段使用独特的自定义范围,例如
@Hello1Scope
和 @Hello2Scope
分别对应 Hello1Fragment
和 Hello2Fragment
-
- 对所有片段使用单个自定义范围,例如
@PerFragment
.
据我了解,与方法 2 一样,定义一个可用于所有片段的范围应该没问题(即 @PerFragment
)。事实上(如果我错了请纠正我),自定义范围的名称似乎无关紧要,只有在创建子组件的地方(即在 Application、Activity 或 Fragment 中)才是重要的。
是否有为每个片段定义唯一范围的用例,例如案例 1?
您的理解是正确的。命名范围允许您传达意图,但它们都以相同的方式工作。
- 对于作用域提供程序方法,每个组件实例将创建所提供对象的 1 个实例。
- 对于无作用域的提供程序方法,每个组件实例都会在需要注入时创建所提供对象的新实例。
不过,组件实例的生命周期很重要。同一组件的 2 个不同实例将提供不同的对象实例,甚至是作用域实例。
范围名称应指示所提供对象的生命周期(与 Component 实例的生命周期匹配),因此 @PerFragment
对我来说更有意义。
通过快速浏览 "MVP Presenters..." 教程,我不清楚作者使用单独范围的意图到底是什么。由于这些名字只是一次性的,我不会读太多。
阅读@vaughandroid 的回答后, 我认为我对自定义范围的理解足以回答我自己的问题。
首先,在处理 dagger2 中的组件、模块和作用域注释时,这里有一些规则。
- A Component 必须 有一个(单个)作用域注释(例如
@Singleton
或 @CustomScope
)。
- A 模块 没有范围注解。
- 一个模块方法 可能有一个与其组件相匹配的(单个)作用域或没有范围,其中:
- Scoped:表示为组件的每个实例创建一个实例。
- Unscoped: 意味着每个 inject() 或 provider 调用都会创建一个新实例
- 注意: Dagger2 仅为根组件(及其模块)保留
@Singleton
。子组件必须使用自定义范围,但该范围的功能与 @Singleton
. 完全相同
现在,回答这个问题:我会说为每个概念上不同的范围创建一个新的命名范围。例如,创建一个 @PerActivity
、@PerFragment
或 @PerView
注释,指示应在何处实例化组件,从而指示其生命周期。
注意:这是两个极端之间的妥协。考虑根组件和 n 个子组件的情况,您将需要:
- 至少 2个注释(
@Singleton
和@SubSingleton
),以及
- 最多 n+1个注释(
@Singleton
,@SubSingleton1
, ... @SubSingletonN
).
示例:
申请:
/** AppComponent.java **/
@Singleton
@Component( modules = AppModule.class )
public interface AppComponent{
void inject(MainActivity mainActivity);
}
/** AppModule.java **/
@Module
public class AppModule{
private App app;
public AppModule(App app){
this.app = app;
}
// For singleton objects, annotate with same scope as component, i.e. @Singleton
@Provides @Singleton public App provideApp() { return app; }
@Provides @Singleton public EventBus provideBus() { return EventBus.getDefault(); }
}
片段:
/** Fragment1Component.java **/
@PerFragment
@Component( modules = {Fragment1Module.class}, dependencies = {AppComponent.class} )
public interface Fragment1Component {
void inject(Fragment1 fragment1);
}
/** Fragment1Module.java **/
@Module
public class Fragment1Module {
// For singleton objects, annotate with same scope as component, i.e. @PerFragment
@Provides @PerFragment public Fragment1Presenter providePresenter(){
return new Fragment1Presenter();
}
}
/** PerFragment.java **/
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerFragment {}
我看过几篇不同的文章,它们似乎提出了在 Dagger 2 中进行自定义范围界定的两种不同方法:
MVP Presenters that Survive Configuration Changes Part-2 (Github repo):
- 为每个片段使用独特的自定义范围,例如
@Hello1Scope
和@Hello2Scope
分别对应Hello1Fragment
和Hello2Fragment
- 为每个片段使用独特的自定义范围,例如
-
- 对所有片段使用单个自定义范围,例如
@PerFragment
.
- 对所有片段使用单个自定义范围,例如
据我了解,与方法 2 一样,定义一个可用于所有片段的范围应该没问题(即 @PerFragment
)。事实上(如果我错了请纠正我),自定义范围的名称似乎无关紧要,只有在创建子组件的地方(即在 Application、Activity 或 Fragment 中)才是重要的。
是否有为每个片段定义唯一范围的用例,例如案例 1?
您的理解是正确的。命名范围允许您传达意图,但它们都以相同的方式工作。
- 对于作用域提供程序方法,每个组件实例将创建所提供对象的 1 个实例。
- 对于无作用域的提供程序方法,每个组件实例都会在需要注入时创建所提供对象的新实例。
不过,组件实例的生命周期很重要。同一组件的 2 个不同实例将提供不同的对象实例,甚至是作用域实例。
范围名称应指示所提供对象的生命周期(与 Component 实例的生命周期匹配),因此 @PerFragment
对我来说更有意义。
通过快速浏览 "MVP Presenters..." 教程,我不清楚作者使用单独范围的意图到底是什么。由于这些名字只是一次性的,我不会读太多。
阅读@vaughandroid 的回答后,
首先,在处理 dagger2 中的组件、模块和作用域注释时,这里有一些规则。
- A Component 必须 有一个(单个)作用域注释(例如
@Singleton
或@CustomScope
)。 - A 模块 没有范围注解。
- 一个模块方法 可能有一个与其组件相匹配的(单个)作用域或没有范围,其中:
- Scoped:表示为组件的每个实例创建一个实例。
- Unscoped: 意味着每个 inject() 或 provider 调用都会创建一个新实例
- 注意: Dagger2 仅为根组件(及其模块)保留
@Singleton
。子组件必须使用自定义范围,但该范围的功能与@Singleton
. 完全相同
现在,回答这个问题:我会说为每个概念上不同的范围创建一个新的命名范围。例如,创建一个 @PerActivity
、@PerFragment
或 @PerView
注释,指示应在何处实例化组件,从而指示其生命周期。
注意:这是两个极端之间的妥协。考虑根组件和 n 个子组件的情况,您将需要:
- 至少 2个注释(
@Singleton
和@SubSingleton
),以及 - 最多 n+1个注释(
@Singleton
,@SubSingleton1
, ...@SubSingletonN
).
示例:
申请:
/** AppComponent.java **/
@Singleton
@Component( modules = AppModule.class )
public interface AppComponent{
void inject(MainActivity mainActivity);
}
/** AppModule.java **/
@Module
public class AppModule{
private App app;
public AppModule(App app){
this.app = app;
}
// For singleton objects, annotate with same scope as component, i.e. @Singleton
@Provides @Singleton public App provideApp() { return app; }
@Provides @Singleton public EventBus provideBus() { return EventBus.getDefault(); }
}
片段:
/** Fragment1Component.java **/
@PerFragment
@Component( modules = {Fragment1Module.class}, dependencies = {AppComponent.class} )
public interface Fragment1Component {
void inject(Fragment1 fragment1);
}
/** Fragment1Module.java **/
@Module
public class Fragment1Module {
// For singleton objects, annotate with same scope as component, i.e. @PerFragment
@Provides @PerFragment public Fragment1Presenter providePresenter(){
return new Fragment1Presenter();
}
}
/** PerFragment.java **/
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerFragment {}