【问题标题】:How do you organise your Dagger 2 modules and components? [closed]你如何组织你的 Dagger 2 模块和组件? [关闭]
【发布时间】:2015-10-28 07:41:33
【问题描述】:

你有一个特定的包来放置所有与 Dagger 相关的类吗?

或者您是否将它们放在它们注入的相关类旁边,例如如果您有MainActivityModuleMainActivityComponent,则将它们放在与MainActivity 相同的包中。

另外,我见过很多人将组件定义为内部类,例如在 Application 类中定义的 ApplicationComponent。你认为这是一个好习惯吗?

【问题讨论】:

    标签: java android dagger-2


    【解决方案1】:

    编辑:让我从这里接近事实的事实开始,但这是 Martin Fowler 的 Data Domain Presentation Layering 文章 HERE (CLICK THE LINK!) 所描述的反模式,它指定您不应该有一个MapperModule 和一个PresenterModule,你应该有一个GalleryModule 和一个SomeFeatureModule,其中包含所有的映射器、演示者等。

    解决此问题的明智方法是使用组件依赖项来为您拥有的每个功能划分原始单例组件的子范围。这就是我描述的“全栈”分层 功能分离。

    下面写的是“反模式”,您将应用程序的顶级模块切割成“层”。这样做有很多缺点。不要这样做。但是您可以阅读它并了解不该做什么。

    原文:

    通常,只要整个应用程序存在,您就会使用单个 Component(如 ApplicationComponent)来包含您在整个应用程序中使用的所有单例依赖项。您将在您的 Application 类中实例化它,并使其可以从其他地方访问。

    我目前的项目结构是:

    + injection
    |- components
       |-- ApplicationComponent.java
    |- modules
       |- data
          |-- DbMapperModule.java
          |-- ...
       |- domain
          |-- InteractorModule.java
          |-- ...
       |- presentation
          |-- ...
       |- utils
          |-- ...
    |- scope
    |- subcomponents
       |- data
          |-- ...
       |- domain
          |-- DbMapperComponent.java
          |-- ...
       |- presentation
          |-- ...
       |- utils
          |-- ...
       |-- AppContextComponent.java
       |-- AppDataComponent.java
       |-- AppDomainComponent.java
       |-- AppPresentationComponent.java
       |-- AppUtilsComponent.java
    

    比如我的是这样的:

    public enum Injector {
        INSTANCE;
        private ApplicationComponent applicationComponent;
    
        private Injector() {
        }
    
        public ApplicationComponent getApplicationComponent() {
            return applicationComponent;
        }
    
        ApplicationComponent initializeApplicationComponent(CustomApplication customApplication) {
            AppContextModule appContextModule = new AppContextModule(customApplication);
            RealmModule realmModule = new RealmModule(customApplication.getRealmHolder());
            applicationComponent = DaggerApplicationComponent.builder()
                    .appContextModule(appContextModule)
                    .realmModule(realmModule)
                    .build();
            return applicationComponent;
        }
    }
    

    你需要一个ApplicationComponent,它可以注入到你想要注入到的任何类的任何包保护字段中。

    @Singleton
    @Component(modules = {
            AppContextModule.class,
            DbMapperModule.class,
            DbTaskModule.class,
            RealmModule.class,
            RepositoryModule.class,
            InteractorModule.class,
            ManagerModule.class,
            ServiceModule.class,
            PresenterModule.class,
            JobManagerModule.class,
            XmlPersisterModule.class
    })
    public interface ApplicationComponent
            extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
        void inject(CustomApplication customApplication);
    
        void inject(DashboardActivity dashboardActivity);
    
        ...
    }
    

    对我来说,AppContextComponent 将是 @Subcomponent,但这并不是它的真正含义。这些只是创建子作用域的一种方法,而不是一种将组件切割成更小的部分的方法。所以我继承的接口实际上只是一个普通的interface,带有提供方法。其他人也一样。

    public interface AppContextComponent {
        CustomApplication customApplication();
    
        Context applicationContext();
    
        AppConfig appConfig();
    
        PackageManager packageManager();
    
        AlarmManager alarmManager();
    }
    

    组件依赖项(允许您子范围,就像子组件一样)不允许多个范围内的组件,这也意味着您的模块将是无范围的。这是因为你不能从多个作用域继承,就像你不能从 Java 中的多个类继承一样。

    无范围的提供程序使得模块不会保留一个单个实例,而是在每次注入调用时保留一个新实例。要获得作用域依赖项,您还需要在模块提供程序方法上提供作用域。

    @Module
    public class InteractorModule {
        @Provides
        @Singleton
        public LeftNavigationDrawerInteractor leftNavigationDrawerInteractor() {
            return new LeftNavigationDrawerInteractorImpl();
        }
    
        ...
    }
    

    在应用程序中,如果您在任何地方都使用单例组件,则不需要更多组件,除非您创建子作用域。如果您愿意,您甚至可以考虑让您的模块成为您的视图和演示者的完整数据提供者。

    @Component(dependencies = {ApplicationComponent.class}, modules = {DetailActivityModule.class}) 
    @ActivityScope
    public interface DetailActivityComponent extends ApplicationComponent {
        DataObject data();
    
        void inject(DetailActivity detailActivity);
    }
    
    @Module
    public class DetailActivityModule {
        private String parameter;
    
        public DetailActivityModule(String parameter) {
            this.parameter = parameter;
        }
    
        @Provides
        public DataObject data(RealmHolder realmHolder) {
            Realm realm = realmHolder.getRealm();
            return realm.where(DataObject.class).equalTo("parameter", parameter).findFirst();
        }
    }
    

    子范围允许您拥有多个演示者实例,然后可以存储状态。这在例如 Mortar/Flow 中是有意义的,其中each screen has its own "path", and each path has its own component - 将数据作为“蓝图”提供。

    public class FirstPath
            extends BasePath {
        public static final String TAG = " FirstPath";
    
        public final int parameter;
    
        public FirstPath(int parameter) {
            this.parameter = parameter;
        }
    
        //...
    
        @Override
        public int getLayout() {
            return R.layout.path_first;
        }
    
        @Override
        public FirstViewComponent createComponent() {
            FirstPath.FirstViewComponent firstViewComponent = DaggerFirstPath_FirstViewComponent.builder()
                    .applicationComponent(InjectorService.obtain())
                    .firstViewModule(new FirstPath.FirstViewModule(parameter))
                    .build();
            return firstViewComponent;
        }
    
        @Override
        public String getScopeName() {
            return TAG + "_" + parameter;
        }
    
        @ViewScope //needed
        @Component(dependencies = {ApplicationComponent.class}, modules = {FirstViewModule.class})
        public interface FirstViewComponent
                extends ApplicationComponent {
            String data();
    
            FirstViewPresenter firstViewPresenter();
    
            void inject(FirstView firstView);
    
            void inject(FirstViewPresenter firstViewPresenter);
        }
    
        @Module
        public static class FirstViewModule {
            private int parameter;
    
            public FirstViewModule(int parameter) {
                this.parameter = parameter;
            }
    
            @Provides
            public String data(Context context) {
                return context.getString(parameter);
            }
    
            @Provides
            @ViewScope //needed to preserve scope
            public FirstViewPresenter firstViewPresenter() {
                return new FirstViewPresenter();
            }
        }
    
        public static class FirstViewPresenter
                extends ViewPresenter<FirstView> {
            public static final String TAG = FirstViewPresenter.class.getSimpleName();
    
            @Inject
            String data;
    
            public FirstViewPresenter() {
                Log.d(TAG, "First View Presenter created: " + toString());
            }
    
            @Override
            protected void onEnterScope(MortarScope scope) {
                super.onEnterScope(scope);
                FirstViewComponent firstViewComponent = scope.getService(DaggerService.TAG);
                firstViewComponent.inject(this);
                Log.d(TAG, "Data [" + data + "] and other objects injected to first presenter.");
            }
    
            @Override
            protected void onSave(Bundle outState) {
                super.onSave(outState);
                FirstView firstView = getView();
                outState.putString("input", firstView.getInput());
            }
    
            @Override
            protected void onLoad(Bundle savedInstanceState) {
                super.onLoad(savedInstanceState);
                if(!hasView()) {
                    return;
                }
                FirstView firstView = getView();
                if(savedInstanceState != null) { //needed check
                    firstView.setInput(savedInstanceState.getString("input"));
                }
            }
    
            public void goToNextActivity() {
                FirstPath firstPath = Path.get(getView().getContext());
                if(firstPath.parameter != R.string.hello_world) {
                    Flow.get(getView()).set(new FirstPath(R.string.hello_world));
                } else {
                    Flow.get(getView()).set(new SecondPath());
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      你有一个特定的包,你把所有与 Dagger 相关的东西放在里面吗? 上课?

      或者您是否将它们放在它们注入的相关类旁边,例如如果你 有一个 MainActivityModule 和 MainActivityComponent,你把它们放进去 与您的 MainActivity 相同的包。

      我对此没有太多经验,但我可以向您展示我的方法。 也许一些有更多经验的人可以改进该解决方案或提供他们的观点。

      我通常这样组织 Dagger 2 的课程:

      - di
      |
      +-- ApplicationComponent class
      |    
      +-- modules
         |
         +-- AndroidModule class
         |
         +-- WebServiceModule class
         |
         +-- ...
         |
      
      • di 包包含与 Dagger 2 和依赖注入相关的类。
      • 在大多数情况下,Android 应用程序通常有一个组件 - 这里它被命名为 ApplicationComponent - 我还没有创建一个包含许多 Dagger 2 组件的 Android 应用程序,我已经看到了只有一个组件的解决方案。
      • modules 包包含 Dagger 2 模块

      我不会为每个活动创建模块。模块对特定功能进行分组。例如。与 SharedPreferences、EventBus(如果您使用类似的东西)、网络连接等接口等系统紧密相连的元素可能位于AndroidModule。如果你的项目有重要的 WebService 接口或者有很多,你可以将它们分组到WebServiceModule。例如,如果您的应用程序负责分析网络并且有许多与网络相关的类似任务的接口,您可以将这些接口分组到NetworkModule。当您的应用程序很简单时,您可能只有一个模块。当它很复杂时,你可以有很多模块。在我看来,一个模块中不应该有很多接口。当出现这种情况时,您可以考虑将它们拆分为单独的模块。您还可以将一些特定于您的项目的业务逻辑保留在单独的模块中。

      另外,我见过不少人将组件定义为内部 类,例如在内部定义的 ApplicationComponent 应用类。您认为这是一个好的做法吗?

      我不确定这是一个好习惯还是坏习惯。我认为没有必要这样做。您可以在扩展Application 类的类中创建公共静态get() 方法,该方法将Application 的实例作为单例返回。这是一个更简单的解决方案,我们应该只有一个 Application 类的实例。如果我们想在单元测试中模拟 Context,我们可以接受 Context 作为参数,在应用程序代码中,根据情况传递 Application Context 或 Activity Context。

      请注意,这只是我的方法,一些更有经验的开发人员可能会以不同且更好的方式组织他们的项目。

      【讨论】:

      • 拥有一个组件听起来确实是个不错的选择。有人可以确认这是否是一个好方法吗?因为,最近我正在做一个项目,我制作了多个组件。但是当我想在一个单一的活动中使用多个组件时,我不是两个单独的注入,而是使一个组件(例如 FragmentProvider)依赖于另一个组件(例如 AppComponent)。仍在寻找管理组件的标准方法。这就是我们应该为一个应用模块拥有一个组件还是多个组件。
      • 这只是我在 Android 和后端应用程序中看到和应用的方法之一。 Dagger 允许您创建多个组件,因此可以肯定它有有效的用例。我认为,如果我们遇到这种情况,我们可以使用多个组件来组织很多模块,但我不确定。
      猜你喜欢
      • 2010-09-15
      • 1970-01-01
      • 1970-01-01
      • 2011-05-13
      • 1970-01-01
      • 1970-01-01
      • 2010-09-23
      • 2010-10-25
      • 1970-01-01
      相关资源
      最近更新 更多