我正试图理解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 中丢弃该引用可确保作用域图可以自由地进行垃圾回收。
编辑
Jesse Wilson 最近发表了一篇自我批评
Dagger 1.0 糟糕地搞砸了其范围名称...@Singleton 注释用于根图和自定义图,因此很难确定一个东西的实际范围。
我读到/听到的所有内容都表明 Dagger 2 改进了作用域工作方式,但我很难理解它们之间的区别。根据 @Kirill Boyarshinov 在下面的评论中所述,组件或依赖项的生命周期仍然像往常一样由具体引用确定。那么 Dagger 1.x 和 2.0 范围之间的区别纯粹是语义上的清晰吗?
我的理解
Dagger 1.x
依赖项要么是 @Singleton
,要么不是。这同样适用于根图和子图中的依赖项,导致依赖项绑定到哪个图不清楚(请参见Dagger 中的单例在子图内是缓存还是每次构建新活动子图时都会重新创建?)
Dagger 2.0
自定义作用域允许您创建语义上清晰的作用域,但在功能上等效于在 Dagger 1.x 中应用 @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
现在用于应用程序级别的单例实例,自定义作用域是注释具有较短生命周期的单例依赖项的首选方法。开发人员负责通过删除不再需要的引用来管理组件/依赖项的生命周期,并确保组件仅在其预期的范围内创建一次,但自定义作用域注释使得更容易识别该作用域。您的理解正确吗?
plus()
引用新图形的引用存储在 Activity 中,并绑定到其生命周期(在onDestroy
中取消引用)。至于作用域,它们确保您的组件实现在编译时生成时没有错误,并且每个依赖项都得到满足。因此,它不仅用于文档目的。查看 此线程 中的一些示例。 - Kirill Boyarshinov