我和dagger2合作有一段时间了。而且我也搞不清楚要不要为每个activity/片段创建一个自己的组件/模块。请帮我澄清一下:
比如我们有一个app,这个app大概有50个屏幕。我们将按照MVP模式和Dagger2为DI实现代码。假设我们有50个活动和50个主持人。
在我看来,通常我们应该这样组织代码:
>
创建AppComponent和AppModule,它们将提供应用程序打开时使用的所有对象。
@Module
public class AppModule {
private final MyApplicationClass application;
public AppModule(MyApplicationClass application) {
this.application = application;
}
@Provides
@Singleton
Context provideApplicationContext() {
return this.application;
}
//... and many other providers
}
@Singleton
@Component( modules = { AppModule.class } )
public interface AppComponent {
Context getAppContext();
Activity1Component plus(Activity1Module module);
Activity2Component plus(Activity2Module module);
//... plus 48 methods for 48 other activities. Suppose that we don't have any other Scope (like UserScope after user login, ....)
}
创建ActivityScope:
@Scope
@Documented
@Retention(value=RUNTIME)
public @interface ActivityScope {
}
为每个activity创建组件和模块。通常我会将它们作为静态类放在activity类中:
@Module
public class Activity1Module {
public LoginModule() {
}
@Provides
@ActivityScope
Activity1Presenter provideActivity1Presenter(Context context, /*...some other params*/){
return new Activity1PresenterImpl(context, /*...some other params*/);
}
}
@ActivityScope
@Subcomponent( modules = { Activity1Module.class } )
public interface Activity1Component {
void inject(Activity1 activity); // inject Presenter to the Activity
}
// .... Same with 49 remaining modules and components.
这些只是非常简单的例子来说明我将如何实现这一点。
但我的一个朋友刚刚给了我另一个实现:
>
创建PresenterModule,它将为所有演示者提供:
@Module
public class AppPresenterModule {
@Provides
Activity1Presenter provideActivity1Presentor(Context context, /*...some other params*/){
return new Activity1PresenterImpl(context, /*...some other params*/);
}
@Provides
Activity2Presenter provideActivity2Presentor(Context context, /*...some other params*/){
return new Activity2PresenterImpl(context, /*...some other params*/);
}
//... same with 48 other presenters.
}
创建AppModule和AppComponent:
@Module
public class AppModule {
private final MyApplicationClass application;
public AppModule(MyApplicationClass application) {
this.application = application;
}
@Provides
@Singleton
Context provideApplicationContext() {
return this.application;
}
//... and many other provides
}
@Singleton
@Component(
modules = { AppModule.class, AppPresenterModule.class }
)
public interface AppComponent {
Context getAppContext();
public void inject(Activity1 activity);
public void inject(Activity2 activity);
//... and 48 other methods for 48 other activities. Suppose that we don't have any other Scope (like UserScope after user login, ....)
}
他的解释是:他不必为每个activity创建组件和模块。我觉得我朋友的想法绝对一点也不好,但是如果我错了请纠正我。原因如下:
>
大量内存泄漏:
如果我想创建一个activity的两个实例,会发生什么?(他如何创建两个演示者)
应用程序初始化会花费很多时间(因为它要创建很多演示者、对象、...)
抱歉的一个很长的帖子,但请帮助我澄清这为我和我的朋友,我无法说服他。您的意见将非常感谢。
/-----------------------------------------/
编辑后做一个演示。
首先,感谢@Pandawarrior回答。我应该在问这个问题之前创建一个演示。我希望我的结论能对其他人有所帮助。
所以,以上我所说的理由大多都是错误的。但这并不意味着我们应该遵循我朋友的想法,原因有二:
>
当源将所有演示者都放在模块/组件中时,这对源的体系结构不利。(它违反了界面分离原则,也可能是单一责任原则)。
当我们创建一个作用域组件时,我们将知道它何时被创建,何时被销毁,这对于避免内存泄漏是一个巨大的好处。因此,对于每个activity,我们都应该创建一个带有@ActivityScope的组件。让我们想象一下,在my friends实现中,我们忘记在Provider中放置一些作用域-method=>内存泄漏将会发生。
在我看来,用一个小的应用程序(只有几个屏幕,没有很多依赖或类似的依赖),我们可以应用我的朋友的想法,但当然不推荐。
更喜欢阅读更多关于:是什么决定了Dagger2中组件(对象图)的生命周期?Dagger2 activity范围,我需要多少模块/组件?
还有一个注意事项:如果您想查看对象何时被销毁,可以一起调用方法的对象,那么GC将立即运行:
System.runFinalization();
System.gc();
如果您只使用这些方法中的一种,GC将稍后运行,您可能会得到错误的结果。
为每个activity
声明一个单独的模块根本不是一个好主意。为每个activity
声明单独的组件更糟糕。这背后的推理非常简单--您并不真正需要所有这些模块/组件(正如您自己已经看到的那样)。
但是,只有一个组件与应用程序
的生命周期绑定,并将其注入所有活动
也不是最佳解决方案(这是您朋友的方法)。它不是最佳的,因为:
@singleton
或自定义作用域)服务
中,但是服务
可能需要与活动
不同的对象(例如,服务
不需要演示者,不具有FragmentManager
等)。通过使用单个组件,您就可以放松为不同组件定义不同对象图的灵活性。因此,每个activity
都有一个组件是矫枉过正的,但对于整个应用程序来说,单个组件不够灵活。最优解介于这两个极端之间(通常如此)。
我使用以下方法:
应用程序
中实例化。活动
和片段
)。在每个activity
和片段
中实例化。服务
所需的对象。在每个服务
中实例化。下面是如何实现相同方法的示例。
编辑2017年7月
我发布了一个视频教程,展示了如何在Android应用中构造Dagger依赖注入代码:Android Dagger for professional教程。
2018年2月编辑
我发表了一个关于Android中依赖注入的完整课程。
在本课程中,我将解释依赖注入的理论,并展示它是如何在Android应用程序中自然出现的。然后我演示了Dagger构造如何适合于一般的依赖注入方案。
如果您学习了这门课程,您就会明白为什么为每个activity/片段单独定义模块/组件的想法在最根本的方面是有缺陷的。
这种方法使类的“功能”集合的表现层结构映射到类的“建构”集合的结构中,从而将它们耦合在一起。这违背了依赖注入的主要目标,即保持类的“构造”集和“功能”集不相交。
适用范围:
@ApplicationScope
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
// Each subcomponent can depend on more than one module
ControllerComponent newControllerComponent(ControllerModule module);
ServiceComponent newServiceComponent(ServiceModule module);
}
@Module
public class ApplicationModule {
private final Application mApplication;
public ApplicationModule(Application application) {
mApplication = application;
}
@Provides
@ApplicationScope
Application applicationContext() {
return mApplication;
}
@Provides
@ApplicationScope
SharedPreferences sharedPreferences() {
return mApplication.getSharedPreferences(Constants.PREFERENCES_FILE, Context.MODE_PRIVATE);
}
@Provides
@ApplicationScope
SettingsManager settingsManager(SharedPreferences sharedPreferences) {
return new SettingsManager(sharedPreferences);
}
}
控制器作用域:
@ControllerScope
@Subcomponent(modules = {ControllerModule.class})
public interface ControllerComponent {
void inject(CustomActivity customActivity); // add more activities if needed
void inject(CustomFragment customFragment); // add more fragments if needed
void inject(CustomDialogFragment customDialogFragment); // add more dialogs if needed
}
@Module
public class ControllerModule {
private Activity mActivity;
private FragmentManager mFragmentManager;
public ControllerModule(Activity activity, FragmentManager fragmentManager) {
mActivity = activity;
mFragmentManager = fragmentManager;
}
@Provides
@ControllerScope
Context context() {
return mActivity;
}
@Provides
@ControllerScope
Activity activity() {
return mActivity;
}
@Provides
@ControllerScope
DialogsManager dialogsManager(FragmentManager fragmentManager) {
return new DialogsManager(fragmentManager);
}
// @Provides for presenters can be declared here, or in a standalone PresentersModule (which is better)
}
然后在activity
:
public class CustomActivity extends AppCompatActivity {
@Inject DialogsManager mDialogsManager;
private ControllerComponent mControllerComponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getControllerComponent().inject(this);
}
private ControllerComponent getControllerComponent() {
if (mControllerComponent == null) {
mControllerComponent = ((MyApplication)getApplication()).getApplicationComponent()
.newControllerComponent(new ControllerModule(this, getSupportFragmentManager()));
}
return mControllerComponent;
}
}
有关依赖注入的其他信息:
Dagger 2镜
Android中的依赖注入
问题内容: 著名的Angular App Structure最佳实践推荐 博客文章概述了新推荐的angularjs项目结构,该项目结构现在是面向组件而非功能的,或者在最初的github问题中命名为“ Organized by Feature”。 博客文章建议每个模块中的每个文件应以模块名称开头,例如: 问题是:与在功能上命名文件相反,在模块的每个文件名内重复模块名称有什么好处和坏处?例如: 我问的
问题内容: 我正在看下面的代码片段: 从这里:http : //howtonode.org/node-redis- fun 。 我不太了解发生了什么。从示例中,我认为Redis客户端是数据库和程序员之间的某种接口,但现在看来他们正在为每个提交的代码创建一个新客户端(他们在教程中构建的应用程序接受代码段)提交并将其存储在数据库中)! 另外,Redis数据库存储在哪里?与脚本位于同一目录中吗?我该如何
我有数据。下面的框架。我想添加一列“g”,它根据列中的连续序列对数据进行分类。也就是说,如最后一列“g”所示,h_no
有没有一种方法,每当I$push monodb数组中的新元素时,都会向它添加一个普通的_id?我记得mongoose是自动执行类似操作的,但现在我使用mongodb的原生js,它似乎没有插入任何id。 示例: 执行时,messages数组应具有常规的_id字段、message和date。目前它只创建消息和日期。
问题内容: 在一些示例之后,似乎我们可以注入一个工厂,其中包含一个REST服务的终结点,如下所示 这看起来不错,但可以想象我还有其他端点,即/ users /:id和/ groups /:id,因为您可以想象到不同端点的数量将会增加。 因此,对于每个终结点,都有一个不同的工厂,这是一个好习惯。 还是有另一种推荐的方法? 我确实没有发现任何问题,但是它迫使我创建许多工厂来处理不同的端点。 确实需要任