是什么决定了 Dagger 2中组件(对象图)的生命周期?

我试图理解 Dagg2中的作用域,特别是作用域图的生命周期。如何创建在离开作用域时将被清除的组件。

对于 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 最近发布了 我的错

Dagger 1.0严重搞错了它的作用域名称... ...@Singleton 注释既用于根图,也用于自定义图,所以很难确定一个事物的实际作用域是什么。

以及其他我读到或听到的关于匕首2改进作用域方式的观点,但我很难理解其中的区别。根据@Kirill Boyarshinov 下面的评论,组件或依赖项的生命周期仍然像往常一样由具体的引用决定。那么 Dagger 1.x 和2.0作用域之间的区别仅仅是语义清晰度的问题吗?

我的理解是

匕首1. x

依赖性不是 @Singleton就是 @Singleton。对于根图和子图中的依赖关系也是如此,这导致依赖关系绑定到哪个图的模糊性(参见 在 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现在打算用于应用程序级的单例实例,自定义作用域是用较短的生命周期注释单例依赖关系的首选方法。

开发人员负责通过删除不再需要的引用来管理组件/依赖项的生命周期,并负责确保组件只在其预定作用域中创建一次,但自定义作用域注释使识别该作用域变得更加容易。

64000美元的问题 *

我对匕首2的作用域和生命周期的理解正确吗?

* 实际上不是一个价值64,000美元的问题。

17682 次浏览

至于你的问题

是什么决定了 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();
}
}
  • 允许对作用域依赖项进行子作用域,从而允许您创建一个“子作用域”组件,该组件使用“超作用域”组件中提供的实例。

这可以通过 @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来访问它自己的作用域依赖项。

  • 一个组件只能有一个作用域组件的子范围,不允许有多个作用域组件依赖项。