我和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中的依赖注入