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


134

我试图把头放在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范围之间的区别纯粹是语义上的清楚吗?

我的理解

匕首1.x

依赖关系是否@Singleton存在。根图和子图中的依赖项也同样如此,从而导致该依赖项绑定到哪个图上的模棱两可(请参阅Dagger中子图内的Singleton被缓存,或者在创建新的活动子图时总会重新创建它们)是建造的?

匕首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现在适用于应用程序级单例实例,而自定义范围是注释具有较短生命周期的单例依赖项的首选方法。

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

$ 64k问题*

我对Dagger 2范围和生命周期的理解正确吗?

*实际上不是$ 64'000的问题。


5
你什么都没错过 管理每个组件的生命周期是手动的。根据我自己的经验,Dagger 1也是如此。当使用plus()对新图的引用对应用程序级ObjectGraph对象进行子图预订时,该对象存储在Activity中,并绑定到其生命周期(在中取消引用onDestroy)。至于范围,它们确保您的组件实现在编译时生成时没有错误,并且满足每个依赖关系。因此,它不仅用于文档目的。从此线程中查看一些示例。
Kirill Boyarshinov 2015年

1
为了清楚起见,“无作用域”提供程序方法在每次注入时都会返回新实例?
user1923613

2
为什么设置component = null; 在onDestroy()中?
玛丽安(MarianPaździoch)

Answers:


70

至于你的问题

什么决定了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来访问其自己的范围内依赖项的原因。

  • 一个组件只能对一个范围内的组件进行子范围划分。不允许多个作用域组件依赖。


一个组件只能对一个范围内的组件进行子范围划分。不允许有多个作用域组件依赖关系(即使它们都具有不同的作用域也不能,尽管我有点认为这是一个错误)。我不太明白这是什么意思
袁咏仪

但是生命周期呢?如果活动被破坏,ActivityComponent是否会成为垃圾收集器的候选者?
Sever的

如果您不将其存储在其他位置,则可以
EpicPandaForce

1
因此,如果我们需要通过Activity来实现组件和注入的对象,则可以在Activity内部构建组件。如果我们只希望通过片段生存,我应该在片段内部构建组件,对吗?您在哪里保留组件实例的作用域?
Thracian

如果我希望它可以通过特定活动生存下来,该怎么办?
Thracian
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.