Mr *_*ike 79 android dagger dagger-2
我已经和dagger2合作了一段时间.我为每个Activity/Fragment创建一个自己的组件/模块感到困惑.请帮我澄清一下:
例如,我们有一个应用程序,该应用程序有大约50个屏幕.我们将实现遵循MVP模式的代码和用于DI的Dagger2.假设我们有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, ....)
}
Run Code Online (Sandbox Code Playgroud)创建ActivityScope:
@Scope
@Documented
@Retention(value=RUNTIME)
public @interface ActivityScope {
}
Run Code Online (Sandbox Code Playgroud)为每个活动创建组件和模块.通常我会将它们作为静态类放在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.
Run Code Online (Sandbox Code Playgroud)这些只是非常简单的例子来说明我将如何实现它.
但是我的一个朋友给了我另一个实现:
创建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.
}
Run Code Online (Sandbox Code Playgroud)创建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, ....)
}
Run Code Online (Sandbox Code Playgroud)他的解释是:他不必为每个活动创建组件和模块. 我认为我的朋友的想法绝对不是很好,但如果我错了,请纠正我.原因如下:
很多内存泄漏:
如果我想创建一个Activity的两个实例,会发生什么?(他怎么能创建两个演示者)
应用程序初始化需要花费大量时间(因为它必须创建许多演示者,对象,......)
很抱歉很长的帖子,但请帮我澄清这个对我和我的朋友,我无法说服他. 您的意见将非常感谢.
/ ------------------------------------------------- ---------------------- /
进行演示后编辑.
首先,感谢@pandawarrior回答.在我提出这个问题之前,我应该创建一个Demo.我希望我的结论可以帮助别人.
所以,我上面提到的所有原因大多都是错误的.但这并不意味着我们应该遵循我的朋友的想法,原因有两个:
当我们创建一个Scope Component时,我们将知道它何时被创建以及何时被销毁,这对于避免内存泄漏是一个巨大的好处.因此,对于每个Activity,我们应该使用@ActivityScope创建一个Component.让我们想象一下,在我的朋友实现的情况下,我们忘了将一些Scope放在Provider-method =>内存泄漏中.
在我看来,使用一个小应用程序(只有几个屏幕没有很多依赖或相似的依赖),我们可以应用我的朋友的想法,但当然不建议.
更喜欢阅读: 什么决定了Dagger 2中组件(对象图)的生命周期? Dagger2活动范围,我需要多少个模块/组件?
还有一点需要注意:如果要查看对象何时被销毁,可以一起调用方法,GC将立即运行:
System.runFinalization();
System.gc();
Run Code Online (Sandbox Code Playgroud)
如果您只使用其中一种方法,GC将在稍后运行,您可能会得到错误的结果.
Vas*_*liy 77
为每个模块声明一个单独的模块Activity根本不是一个好主意.为每个声明单独的组件Activity更糟糕.这背后的原因非常简单 - 您并不真正需要所有这些模块/组件(正如您自己已经看到的那样).
然而,只有一个与Application生命周期相关的组件并将其用于注入所有组件Activities也不是最佳解决方案(这是您朋友的方法).它不是最佳的,因为:
@Singleton或自定义范围)Services,但Services可能需要不同的对象Activities(例如Services,不需要演示者,不需要FragmentManager等等).通过使用单个组件,您可以放松为不同组件定义不同对象图的灵活性.因此,每个组件Activity都是过度杀伤,但整个应用程序的单个组件不够灵活.最佳解决方案介于这些极端之间(通常是这样).
我使用以下方法:
Application.Activities和Fragments).在每个Activity和实例中实例化Fragment.Services.在每个实例中实例化Service.以下是如何实现相同方法的示例.
2017年7月编辑
我发布了一个视频教程,演示了如何在Android应用程序中构建Dagger依赖注入代码:Android Dagger for Professionals Tutorial.
编辑2018年2月
在本课程中,我将解释依赖注入理论,并展示它如何在Android应用程序中自然出现.然后我演示了Dagger构造如何适应一般依赖注入方案.
如果您学习本课程,您将理解为什么对每个活动/片段单独定义模块/组件的想法基本上是以最基本的方式存在缺陷.
这种方法使得"功能"类集合的表示层结构被镜像到"构造"类集的结构中,从而将它们耦合在一起.这违背了依赖注入的主要目标,即保持"构造"和"功能"类的集合不相交.
适用范围:
@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);
}
}
Run Code Online (Sandbox Code Playgroud)
控制器范围:
@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)
}
Run Code Online (Sandbox Code Playgroud)
然后在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;
}
}
Run Code Online (Sandbox Code Playgroud)
有关依赖注入的其他信息:
Dav*_*son 14
一些如何组织你的组件,模块,和包最好的例子可以在谷歌Android架构蓝图GitHub库中找到这里.
如果您检查那里的源代码,您可以看到有一个应用程序范围的组件(具有整个应用程序持续时间的生命周期),然后将活动和片段的活动范围组件分离为对应于给定功能的项目.例如,有以下包:
addedittask
taskdetail
tasks
Run Code Online (Sandbox Code Playgroud)
每个包内都有一个模块,组件,演示器等.例如,里面taskdetail有以下类:
TaskDetailActivity.java
TaskDetailComponent.java
TaskDetailContract.java
TaskDetailFragment.java
TaskDetailPresenter.java
TaskDetailPresenterModule.java
Run Code Online (Sandbox Code Playgroud)
组织这种方式的优势(而不是将所有活动分组到一个组件或模块中)是您可以利用Java可访问性修饰符并实现有效Java项13.换句话说,功能分组的类将是相同的包,您可以利用protected和package-private 访问性修饰符,以防止您的类的无意中使用.
Lie*_*ung -6
您的朋友是对的,您实际上不必为每个活动创建组件和模块。Dagger 应该可以帮助您减少混乱的代码,并通过将类实例化委托给模块而不是在 Activity 的 onCreate 方法中实例化它们来使您的 Android Activity 更干净。
通常我们会这样做
public class MainActivity extends AppCompatActivity {
Presenter1 mPresenter1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter1 = new Presenter1(); // you instantiate mPresentation1 in onCreate, imagine if there are 5, 10, 20... of objects for you to instantiate.
}
}
Run Code Online (Sandbox Code Playgroud)
你这样做
public class MainActivity extends AppCompatActivity {
@Inject
Presenter1 mPresenter1; // the Dagger module take cares of instantiation for your
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
injectThisActivity();
}
private void injectThisActivity() {
MainApplication.get(this)
.getMainComponent()
.inject(this);
}}
Run Code Online (Sandbox Code Playgroud)
那么写太多东西有点挫败匕首的目的不是吗?如果我必须为每个活动创建模块和组件,我宁愿在活动中实例化我的演示者。
至于您的问题:
1-内存泄漏:
不,除非您@Singleton为您提供的演示者添加注释。@InjectDagger 仅在您在目标类中执行操作时才会创建对象。它不会在您的场景中创建其他演示者。您可以尝试使用Log来查看它们是否被创建。
@Module
public class AppPresenterModule {
@Provides
@Singleton // <-- this will persists throughout the application, too many of these is not good
Activity1Presenter provideActivity1Presentor(Context context, ...some other params){
Log.d("Activity1Presenter", "Activity1Presenter initiated");
return new Activity1PresenterImpl(context, ...some other params);
}
@Provides // Activity2Presenter will be provided every time you @Inject into the activity
Activity2Presenter provideActivity2Presentor(Context context, ...some other params){
Log.d("Activity2Presenter", "Activity2Presenter initiated");
return new Activity2PresenterImpl(context, ...some other params);
}
.... Same with 48 others presenters.
Run Code Online (Sandbox Code Playgroud)
}
2-您注入两次并记录它们的哈希码
//MainActivity.java
@Inject Activity1Presenter mPresentation1
@Inject Activity1Presenter mPresentation2
@Inject Activity2Presenter mPresentation3
@Inject Activity2Presenter mPresentation4
//log will show Presentation2 being initiated twice
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
injectThisActivity();
Log.d("Activity1Presenter1", mPresentation1.hashCode());
Log.d("Activity1Presenter2", mPresentation2.hashCode());
//it will shows that both have same hash, it's a Singleton
Log.d("Activity2Presenter1", mPresentation3.hashCode());
Log.d("Activity2Presenter2", mPresentation4.hashCode());
//it will shows that both have different hash, hence different objects
Run Code Online (Sandbox Code Playgroud)
@Inject3. 不,只有当您进入活动时才会创建对象,而不是应用程序初始化时创建。
| 归档时间: |
|
| 查看次数: |
21018 次 |
| 最近记录: |