java 你如何组织你的 Dagger 2 模块和组件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31834056/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you organise your Dagger 2 modules and components?
提问by ivacf
Do you have a specific package where you put all the Dagger related classes?
你有一个特定的包来放置所有与 Dagger 相关的类吗?
Or do you put them next to the relevant class they inject, e.g. if you have an MainActivityModule
and MainActivityComponent
, you put them in the same package as your MainActivity
.
或者您是否将它们放在它们注入的相关类旁边,例如,如果您有一个MainActivityModule
and MainActivityComponent
,则将它们与您的MainActivity
.
Also, I've seen quite a few people defining components as inner classes, e.g. an ApplicationComponent
that is defined inside the Application
class. Do you think this is a good practice?
另外,我看到很多人将组件定义为内部类,例如ApplicationComponent
在Application
类内部定义的。你认为这是一个好习惯吗?
回答by EpicPandaForce
EDIT: Let me start out with the fact that this is close to the truth here, but this is an antipatternas described by Martin Fowler's Data Domain Presentation Layering
article HERE (CLICK THE LINK!), which specifies that you shouldn'thave a MapperModule
and a PresenterModule
, you should have a GalleryModule
and a SomeFeatureModule
which has all the mappers, presenters etc. in it.
编辑:让我从一个事实开始,这与这里的真相很接近,但这是Martin Fowler 的文章HERE(点击链接!)所描述的反模式,它指定你不应该有 a和 a ,你应该有 a和 a ,其中包含所有映射器、演示者等。Data Domain Presentation Layering
MapperModule
PresenterModule
GalleryModule
SomeFeatureModule
The smart route to go about it is to use component dependencies to subscope your original singleton component for each feature you have. This what I described is the "full-stack" layering, separation byfeatures.
明智的做法是使用组件依赖关系为您拥有的每个功能对原始单例组件进行细分。这就是我所描述的“全栈”分层,按功能分离。
The one written down below is the "anti-pattern", where you cut your application's top level modules into "layers". It has numerous disadvantages to do so. Don't do it. But you can read it and learn what not to do.
下面写的是“反模式”,您可以将应用程序的顶级模块切成“层”。这样做有很多缺点。不要这样做。但是您可以阅读它并了解不该做什么。
ORIGINAL TEXT:
原文:
Normally, you'd use a single Component
like an ApplicationComponent
to contain all singleton dependencies that you use throughout the app as long as the entire application exists. You would instantiate this in your Application class, and make this accessible from elsewhere.
通常,只要整个应用程序存在,您就会使用Component
像 an 一样的单个ApplicationComponent
来包含您在整个应用程序中使用的所有单例依赖项。您将在您的 Application 类中实例化它,并使其可从其他地方访问。
Project structure for me currently is:
我目前的项目结构是:
+ 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
For example, mine is like this:
例如,我的是这样的:
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;
}
}
And you need an ApplicationComponent
that can inject into whatever package-protected fields of whatever class you want to field-inject to.
并且您需要一个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);
...
}
For me, AppContextComponent
would be a @Subcomponent
, but that is not actually what it means. Those are just a way to create a subscope, and not a way to cut your component into smaller parts. So the interface I inherit is actually just a normal interface
with provision methods. Same for the others.
对我来说,AppContextComponent
应该是 a @Subcomponent
,但这实际上并不是它的意思。这些只是创建子范围的一种方式,而不是将组件切割成更小的部分的方式。所以我继承的接口其实只是一个普通interface
的provision 方法。其他人也一样。
public interface AppContextComponent {
CustomApplication customApplication();
Context applicationContext();
AppConfig appConfig();
PackageManager packageManager();
AlarmManager alarmManager();
}
Component dependencies (which allows you to subscope, just like subcomponents) don't allow multiple scoped components, which also means your modules would be unscoped. This is due to how you cannot inherit from multiple scopes, just like you can't inherit from multiple classes in Java.
组件依赖(它允许你划分范围,就像子组件一样)不允许多个范围的组件,这也意味着你的模块将是无范围的。这是因为你不能从多个作用域继承,就像你不能从 Java 中的多个类继承一样。
Unscoped providers make it so that the module does not retain a singleinstance but a new one on every inject call. To get scoped dependencies, you need to provide the scope on the module provider methods too.
Unscoped 提供程序使得模块不会保留单个实例,而是在每次注入调用时保留一个新实例。要获取作用域依赖项,您还需要在模块提供程序方法上提供作用域。
@Module
public class InteractorModule {
@Provides
@Singleton
public LeftNavigationDrawerInteractor leftNavigationDrawerInteractor() {
return new LeftNavigationDrawerInteractorImpl();
}
...
}
In an application, if you use Singleton components everywhere, you won't need more components, unless you create subscopes. If you want, you can even consider making your modules a complete data provider for your views and presenters.
在应用程序中,如果到处都使用单例组件,则不需要更多组件,除非您创建子作用域。如果需要,您甚至可以考虑使您的模块成为您的视图和演示者的完整数据提供者。
@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();
}
}
Subscoping allows you to have multiple instances of your presenter, which can then store state. This makes sense in for example Mortar/Flow, where each screen has its own "path", and each path has its own component- to provide the data as a "blueprint".
子范围允许您拥有多个演示者实例,然后可以存储状态。这在例如Mortar/Flow 中是有意义的,其中每个屏幕都有自己的“路径”,并且每个路径都有自己的组件- 将数据作为“蓝图”提供。
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());
}
}
}
}
回答by Piotr Wittchen
Do you have a specific package where you put all the Dagger related classes?
Or do you put them next to the relevant class they inject, e.g. if you have an MainActivityModule and MainActivityComponent, you put them in the same package as your MainActivity.
你有一个特定的包来放置所有与 Dagger 相关的类吗?
或者您是否将它们放在它们注入的相关类旁边,例如,如果您有一个 MainActivityModule 和 MainActivityComponent,您将它们与您的 MainActivity 放在同一个包中。
I don't have much experience with that, but I can show you my approach. Maybe some people with more experience can improve that solution or provide their point of view.
我对此没有太多经验,但我可以向您展示我的方法。也许一些经验丰富的人可以改进该解决方案或提供他们的观点。
I usually organize Dagger 2 classes like that:
我通常像这样组织 Dagger 2 类:
- di
|
+-- ApplicationComponent class
|
+-- modules
|
+-- AndroidModule class
|
+-- WebServiceModule class
|
+-- ...
|
di
package contains classes related with Dagger 2 and dependency injection.- in most cases Android application usually has one Component - here it's named
ApplicationComponent
- I haven't created an Android application with many Dagger 2 components yet and I've seen solutions with only one component. modules
package contains Dagger 2 modules
di
包包含与 Dagger 2 和依赖注入相关的类。- 在大多数情况下,Android 应用程序通常有一个组件——这里它被命名为
ApplicationComponent
——我还没有创建一个包含许多 Dagger 2 组件的 Android 应用程序,我见过只有一个组件的解决方案。 modules
包包含 Dagger 2 模块
I don't create module per Activity. Modules group specific functionality. E.g. elements strongly connected with system like interface for SharedPreferences, EventBus (if you are using something like that), network connectivity, etc. may be located in AndroidModule
. If your project has important interfaces for WebService or there's a lot of them, you can group them in WebServiceModule
. If your application is for example responsible for analyzing network and has many interfaces for similar tasks related to the network, you can group these interfaces in NetworkModule
. When your application is simple, it may happen that you will have only one module. When it's complicated, you can have many modules. In my opinion, you shouldn't have many interfaces in a single module. When there's such situation you may consider splitting them into separate modules. You can also keep some business logic specific for your project in a separate module.
我不为每个活动创建模块。模块对特定功能进行分组。例如,与 SharedPreferences 接口、EventBus(如果您使用类似的东西)、网络连接等系统密切相关的元素可能位于AndroidModule
. 如果您的项目有重要的 WebService 接口或者有很多接口,您可以将它们分组在WebServiceModule
. 例如,如果您的应用程序负责分析网络并且有许多接口用于与网络相关的类似任务,您可以将这些接口分组NetworkModule
. 当您的应用程序很简单时,您可能只有一个模块。当它很复杂时,你可以有很多模块。在我看来,你不应该在一个模块中有很多接口。当出现这种情况时,您可以考虑将它们拆分为单独的模块。您还可以在单独的模块中保留一些特定于您的项目的业务逻辑。
Also, I've seen quite a few people defining components as inner classes, e.g. an ApplicationComponent that is defined inside the Application class. Do you think this is a good practice?
此外,我看到很多人将组件定义为内部类,例如在 Application 类中定义的 ApplicationComponent。你认为这是一个好习惯吗?
I'm not sure if it's a good or bad practice. I think there's no need to do that. You can create public static get()
method inside class extending Application
class, which will return instance of Application
as a singleton. It's much simpler solution and we should have only one instance of an Application
class. If we want to to mock Context in a unit tests, we can accept Context as a parameter and in an application code, pass Application Context or Activity Context depending on the situation.
我不确定这是好还是坏的做法。我认为没有必要这样做。您可以get()
在类扩展Application
类中创建公共静态方法,该方法将Application
作为单例返回实例。这是一个更简单的解决方案,我们应该只有一个Application
类的实例。如果我们想在单元测试中模拟 Context,我们可以接受 Context 作为参数,并在应用程序代码中,根据情况传递 Application Context 或 Activity Context。
Please note, it's just my approach and some more experienced developers may organize their projects in a different and better way.
请注意,这只是我的方法,一些更有经验的开发人员可能会以不同的更好的方式组织他们的项目。