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

142

我正试图理解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现在用于应用程序级别的单例实例,自定义作用域是注释具有较短生命周期的单例依赖项的首选方法。开发人员负责通过删除不再需要的引用来管理组件/依赖项的生命周期,并确保组件仅在其预期的范围内创建一次,但自定义作用域注释使得更容易识别该作用域。您的理解正确吗?

5
你没有错过任何东西。每个组件的生命周期管理都是手动的。从我的经验来看,Dagger 1 也是如此。当对应用程序级别的 ObjectGraph 对象进行子图形建模时,使用 plus() 引用新图形的引用存储在 Activity 中,并绑定到其生命周期(在 onDestroy 中取消引用)。至于作用域,它们确保您的组件实现在编译时生成时没有错误,并且每个依赖项都得到满足。因此,它不仅用于文档目的。查看 此线程 中的一些示例。 - Kirill Boyarshinov
1
只是为了明确一下,"unscoped"提供程序方法在每次注入时返回新实例吗? - user1923613
2
为什么在onDestroy()中设置component = null;? - Marian Paździoch
1个回答

75

关于你的问题

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来访问其自己的作用域依赖项。

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


一个组件只能子作用域一个有作用域的组件。不允许多个有作用域的组件依赖(即使它们都有不同的作用域,尽管我认为这可能是一个错误)。 - Damon Yuan
1
但是Livecycle怎么办?如果Activity被销毁,ActivityComponent是否会成为垃圾收集器的候选对象? - Sever
1
如果你没有把它存储在其他地方,那么是的。 - EpicPandaForce
1
所以,如果我们需要在Activity中使用组件和注入对象,我们就会在Activity内部构建组件。如果我们只希望在Fragment中生存,我应该在Fragment内部构建组件,对吧?您保留组件实例的位置会影响其作用域。 - Thracian
1
@Thracian,你有没有得到关于你问题的答案或经验?我已经为我的模块和子组件创建了作用域,并注入了活动,但似乎其中一个提供的对象超出了其预定的生命周期。 - saintjab
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接