你如何组织你的Dagger 2模块和组件?

你有一个特定的包,你把所有的匕首相关的类?

或者你把它们放在他们注入的相关类旁边,例如,如果你有一个MainActivityModuleMainActivityComponent ,你把它们放在和MainActivityComponent相同的包中。

另外,我已经看到相当多的人将组件定义为内部类,例如Application类中定义的Application 。 你认为这是一个很好的做法吗?

Solutions Collecting From Web of "你如何组织你的Dagger 2模块和组件?"

编辑:让我开始与事实,这是接近于事实在这里,但这是一个反模式 ,如马丁福勒的Data Domain Presentation Layering文章这里(点击链接!)所述 ,它指定你不应该有一个MapperModulePresenterModule ,你应该有一个GalleryModule和一个SomeFeatureModule ,它包含所有的mapper,演示SomeFeatureModule等等。

要做到这一点的智能路线是使用组件依赖关系来为您拥有的每个function对原始单件组件进行子展示。 我所描述的是“全栈式”分层 特征 分隔

下面写的是“反模式”,将应用程序的顶层模块切成“图层”。 这样做有许多缺点。 不要这样做。 但是你可以阅读它并学习不该做的事情。

原文:

通常情况下,只要整个应用程序存在,就可以像ApplicationComponent一样使用单个Component来包含整个应用程序使用的所有单例依赖项。 你将在你的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 ,但实际上并不是这个意思。 这些只是创build子范围的一种方法,而不是将组件分割成更小的部分的方法。 所以我inheritance的接口实际上只是一个提供方法的普通interface 。 其他人也一样。

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

组件依赖性(允许您像子组件一样进行子范围化)不允许使用多个范围内的组件,这也意味着您的模块将被放大。 这是由于你不能从多个范围inheritance,就像你不能从Java中的多个类inheritance一样。

非显而易见的提供程序使得模块不会保留一个实例,而是在每个注入调用中都保留一个新的实例。 要获得范围依赖关系,您还需要在模块提供者方法上提供范围。

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

在一个应用程序中,如果您在任何地方使用Singleton组件,除非创build子视图,否则不需要更多的组件。 如果你愿意,你甚至可以考虑让你的模块成为你的观点和主持人的完整数据提供者。

 @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 ( 每个屏幕都有自己的“path”,每个path都有自己的组件 )以提供“蓝图”数据。

 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()); } } } } 

你有一个特定的包,你把所有的匕首相关的类?

或者你把它们放在他们注入的相关类旁边,例如,如果你有一个MainActivityModule和MainActivityComponent,你把它们放在和MainActivity相同的包中。

我没有太多的经验,但我可以告诉你我的方法。 也许一些有经验的人可以改善这个解决scheme或者提供他们的观点。

我通常组织Dagger 2这样的类:

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

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

另外,我已经看到相当多的人将组件定义为内部类,例如Application类中定义的ApplicationComponent。 你认为这是一个很好的做法吗?

我不确定这是好还是不好的做法。 我觉得没有必要这样做。 您可以在扩展Application类的类中创build公共静态get()方法,这将以单例forms返回Application实例。 这是更简单的解决scheme,我们应该只有一个Application类的实例。 如果我们想在unit testing中模拟上下文,我们可以接受上下文作为参数,并在应用程序代码中根据情况传递应用程序上下文或活动上下文。

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