【问题标题】:Dagger 2 Custom Scope for each Fragment (or Activity etc...)每个片段(或活动等)的 Dagger 2 自定义范围
【发布时间】:2015-09-07 11:15:35
【问题描述】:

我查看了几篇不同的文章,这些文章似乎提出了在 Dagger 2 中进行自定义作用域的两种不同方法:

  1. MVP Presenters that Survive Configuration Changes Part-2(Github repo):

    • 为每个片段使用唯一的自定义范围,例如@Hello1Scope@Hello2Scope 分别对应 Hello1FragmentHello2Fragment
  2. Tasting Dagger 2 on Android:

    • 对所有片段使用单个自定义范围,例如@PerFragment

据我了解,与方法 2 一样,定义一个可用于所有片段的范围(即@PerFragment)似乎是可以的。事实上(如果我错了,请纠正我),似乎自定义范围的名称无关紧要,只有创建子组件的位置(即在 Application、Activity 或 Fragment 中)才重要。

是否有任何用例可以为每个片段定义唯一范围,例如案例 1?

【问题讨论】:

    标签: android android-fragments dagger-2


    【解决方案1】:

    阅读@vaughandroid 和What determines the lifecycle of a component (object graph) in Dagger 2? 的答案后,我认为我对自定义范围的理解足以回答我自己的问题。

    首先,在处理 dagger2 中的组件、模块和作用域注释时,这里有一些规则。

    • 组件 必须有一个(单一的)范围注释(例如@Singleton@CustomScope)。
    • 模块 没有范围注释。
    • 模块方法 可能有一个与其组件匹配的(单一)范围或没有范围,其中:
      • Scoped:表示为组件的每个实例创建一个实例。
      • 无作用域:意味着每次注入()或提供程序调用都会创建一个新实例
      • 注意: Dagger2 仅为根组件(及其模块)保留@Singleton。子组件必须使用自定义范围,但该范围的功能与@Singleton 完全相同。

    现在,回答这个问题:我想说为每个概念上不同的范围创建一个新的命名范围。例如,创建一个 @PerActivity@PerFragment@PerView 注释来指示组件应在何处实例化,从而指示其生命周期。

    注意:这是两个极端之间的折衷。考虑您需要的根组件和 n 个子组件的情况:

    • 至少 2 个注释(@Singleton@SubSingleton),并且
    • 最多 n+1 个注释(@Singleton@SubSingleton1、...@SubSingletonN)。

    示例:

    应用:

    /** AppComponent.java **/ 
    @Singleton
    @Component( modules = AppModule.class )
    public interface AppComponent{
        void inject(MainActivity mainActivity);
    }
    
    /** AppModule.java **/
    @Module
    public class AppModule{
        private App app;
    
        public AppModule(App app){
            this.app = app;
        }
    
        // For singleton objects, annotate with same scope as component, i.e. @Singleton
        @Provides @Singleton public App provideApp() { return app; }
        @Provides @Singleton public EventBus provideBus() { return EventBus.getDefault(); }
    }
    

    片段:

    /** Fragment1Component.java **/
    @PerFragment
    @Component( modules = {Fragment1Module.class}, dependencies = {AppComponent.class} )
    public interface Fragment1Component {
        void inject(Fragment1 fragment1);
    }
    
    /** Fragment1Module.java **/ 
    @Module
    public class Fragment1Module {
        // For singleton objects, annotate with same scope as component, i.e. @PerFragment
        @Provides @PerFragment public Fragment1Presenter providePresenter(){
            return new Fragment1Presenter();
        }
    }
    
    /** PerFragment.java **/ 
    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PerFragment {}
    

    【讨论】:

    • 您能否提供有关这些规则的详细信息?
    • @peacepassion 不确定您到底需要什么样的细节,但我用我已经完成的项目中的一个示例更新了答案。
    • 你会在哪里实例化Fragment1Component,你会在哪里使用它? onCreateView?
    • @tibo 我建议在onCreate 中实例化它。
    • 基于关于范围和配置更改以及自定义范围的类似阅读,我最终将这个小library 放在一起,以帮助为每个片段/活动、演示者绑定等创建范围/组件。也许它是什么你觉得有用的
    【解决方案2】:

    你的理解是正确的。命名范围允许您传达意图,但它们都以相同的方式工作。

    • 对于作用域提供程序方法,每个 Component 实例将创建 1 个所提供对象的实例。
    • 对于无范围的提供程序方法,每个组件实例将在需要注入时创建所提供对象的新实例。

    组件实例的生命周期很重要。同一组件的 2 个不同实例将提供不同的对象实例,甚至是作用域实例。

    范围名称应指示所提供对象的生命周期(与组件实例的生命周期相匹配),因此 @PerFragment 对我来说更有意义。

    通过快速浏览“MVP Presenters...”教程,我并不清楚作者的意图是使用单独的范围。由于名称只是一次性的,我不会过多地阅读它。

    【讨论】:

    • 我猜他计划“将逻辑封装到自定义视图中”,因此每个视图都有自己的演示者,每个演示者都有自己的范围。
    • @EpicPandaForce 是的,你说得对,方法 1 的目的是将每个“MVP-View”与“Android-Custom-View”相关联,其中每个 View 都有自己的 Presenter(尽管,通常我见过与 Fragments/Activity 关联的 MVP-View)。然而,同样的论点似乎也适用——即,使用 @PerView 而不是每个视图都有自己的范围,对吧?
    • 也许我只是一个平庸的程序员,但尽管我喜欢 DI、MVP 和所有干净代码的东西,但这个“MVP Presenters...”系列对我来说感觉设计过度了。我很遗憾最终不得不使用 public abstract class PresenterControllerFragment<C extends HasPresenter<P>, P extends Presenter> extends ComponentControllerFragment<C>ComponentCacheDelegate 之类的代码库来维护代码库。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多