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

What determines the lifecycle of a component (object graph) in Dagger 2?

我正在努力研究 Dagger 2 中的作用域,特别是作用域图的生命周期。你如何创建一个组件,当你离开范围时将被清理。

在 Android 应用程序的情况下,使用 Dagger 1.x 你通常在应用程序级别有一个根作用域,你可以扩展它以在 activity级。

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 最近发布了一条 mea culpa

Dagger 1.0 badly screwed up its scope names ... The @Singleton annotation is used for both root graphs and custom graphs, so it's tricky to figure out what the actual scope of a thing is.

以及我 read/heard 的所有其他内容都指向 Dagger 2 改进范围的工作方式,但我正在努力理解其中的区别。根据@Kirill Boyarshinov 在下面的评论,组件或依赖项的生命周期仍然像往常一样由具体引用确定。那么 Dagger 1.x 和 2.0 作用域之间的区别纯粹是语义清晰度的问题吗?

我的理解

匕首1.x

依赖关系是 @Singleton 或不是。这同样适用于根图和子图中的依赖关系,导致依赖关系绑定到哪个图的歧义(参见 In Dagger are Singletons within the sub-graph cached or will they always be recreated when a new activity sub-graph is constructed?

匕首 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 传达了您关于此组件生命周期的 意图 ,但最终您可以使用组件 anywhere/anytime。 Dagger 唯一的承诺是,对于给定的组件,范围注解方法将 return 单个实例。我还假设 Dagger 2 在组件上使用范围注释来验证模块只提供相同范围内或非范围内的依赖项。

总结

依赖关系仍然是单例或非单例,但 @Singleton 现在适用于应用程序级单例实例,自定义范围是注释具有较短生命周期的单例依赖项的首选方法。

开发人员负责通过删除不再需要的引用来管理 components/dependencies 的生命周期,并负责确保组件仅在其预期范围内创建一次,但自定义范围注释更容易识别该范围。

6.4 万美元的问题*

我对 Dagger 2 作用域和生命周期的理解是否正确?

* 实际上不是 64,000 美元的问题。

关于你的问题

What determines the lifecycle of a component (object graph) in 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();
    }
}
  • 允许对范围内的依赖项进行子范围划分,从而允许您创建一个 "subscoped" 组件,该组件使用 "superscoped" 组件提供的实例。

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

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