匕首-我们是否应该为每个活动/片段创建每个组件和模块 [英] Dagger- Should we create each component and module for each Activity/ Fragment

查看:69
本文介绍了匕首-我们是否应该为每个活动/片段创建每个组件和模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用dagger2已有一段时间了.而且我为每个活动/片段创建一个自己的组件/模块感到困惑.请帮我澄清一下:

I've been working with dagger2 for a while. And I got confused wether to create an own component/module for each Activity/ Fragment. Please help me clarify this:

例如,我们有一个应用程序,该应用程序约有50个屏幕. 我们将按照MVP模式和Dagger2 for DI来实现代码.假设我们有50个活动和50个演示者.

For example, We have an app, and the app has about 50 screens. We will implement the code following the MVP pattern and Dagger2 for DI. Suppose that we have 50 activities and 50 presenters.

在我看来,通常我们应该这样组织代码:

In my opinion, usually we should organize the code like this :

  1. 创建一个AppComponent和AppModule,它们将提供在打开应用程序时将使用的所有对象.

  1. Create an AppComponent and AppModule which will provide all objects that will be used while the app is open.

@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:

  • Create ActivityScope :

    @Scope
    @Documented
    @Retention(value=RUNTIME)
    public @interface ActivityScope {
    }
    

  • 为每个活动创建组件和模块.通常我会将它们作为Activity类中的静态类:

  • Create Component and Module for each Activity. Usually I will put them as static classes within the Activity class:

    @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.
    

  • 这些只是非常简单的示例,以说明如何实现.

    Those are just very simple examples to show how I would implement this.

    但是我的一个朋友给了我另一个实现方法:

    1. 创建将提供所有演示者的PresenterModule:

    1. Create PresenterModule which will provide all presenters:

    @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:

  • Create AppModule and 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, ....)
    
    }
    

  • 他的解释是:他不必为每个活动创建组件和模块. 我认为我的朋友的想法绝对不是一个好主意,但是如果我错了,请纠正我.原因如下:

    His explaination is: He doesn't have to create components and modules for each activity. I think my friends idea is absolutely not good at all, but please correct me if I am wrong. Here are the reasons:

    1. 大量内存泄漏:

    • 即使用户仅打开2个活动,该应用也将创建50个演示者.
    • 用户关闭活动后,其演示者仍将保留
    • The app will create 50 presenters even if the user has only 2 Activities open.
    • After the user closes an Activity, its presenter will still remain

    如果我想为一个Activity创建两个实例,该怎么办? (他如何创建两个演示者)

    What happens if I want to create two instances of one Activity ? (how can he create two presenters )

    应用初始化需要很多时间(因为它必须创建许多演示者,对象等)

    It will take a lot of time for the app to initialize (because it has to create many presenters, objects, ...)

    很长的帖子,很抱歉,但是请帮我为我和我的朋友澄清一下,我无法说服他. 您的评论将不胜感激.

    Sorry for a long post, but please help me clarify this for me and my friend, I can't convince him. Your comments will be very appreciated.

    / ------------------------------------------- ---------------------------- /

    在演示后进行编辑.

    首先,感谢@pandawarrior的回答. 在问这个问题之前,我应该先创建一个演示.我希望我的结论可以对其他人有所帮助.

    First, thanks for @pandawarrior answer. I should have created a Demo before I asked this question. I hope my conclusion here could help someone else.

    1. 我的朋友所做的事情不会导致内存泄漏,除非他将任何范围都应用于Provides方法. (例如,@ Singleton或@UserScope,...)
    2. 如果Provides方法没有任何范围,我们可以创建许多演示者. (所以,我的第二点也是错误的)
    3. Dagger仅在需要时创建演示者. (因此,该应用将不需要很长时间来初始化,我被懒惰注入所迷惑)

    因此,我以上所述的所有原因大多都是错误的.但这并不意味着我们应该遵循我的朋友的想法,这有两个原因:

    So, all the reasons I have said above are mostly wrong. But it does not mean that we should follow my friend idea, for two reasons:

    1. 当他在模块/组件中初始化所有演示者时,这对于源代码的体系结构是不利的. (它违反了界面隔离原则,也许是在创建范围组件时,我们将知道它的创建时间和销毁时间,这对于避免内存泄漏具有巨大的好处.因此,对于每个活动,我们都应该使用@ActivityScope创建一个组件.让我们想象一下,在我的朋友实现中,我们忘记在Provider-method =>中放置一些Scope会发生内存泄漏.

      When we create a Scope Component, we will know when it's created and when it's destroyed which is a huge benefit for avoiding memory leaks. So, for each Activity we should create a Component with an @ActivityScope. Let's imagine, with my friends implementation, that we forgot to put some Scope in the Provider-method => memory leaks will occur.

      我认为,使用小型应用程序(只有几个没有很多依赖性或类似依赖性的屏幕),我们可以应用我的朋友的想法,但是当然不建议这样做.

      In my opinion, with a small app (just a few screens without many dependencies or with similar dependencies), we could apply my friends idea, but of course it's not recommended.

      希望继续阅读以下内容: 是什么决定了组件的生命周期(对象图)在Dagger 2中? Dagger2活动范围,多少个模块/我需要什么组件?

      Prefer to read more on: What determines the lifecycle of a component (object graph) in Dagger 2? Dagger2 activity scope, how many modules/components do i need?

      还有一点要注意:如果要查看对象何时被销毁,可以一起调用方法的对象,GC会立即运行:

      And one more note: If you want to see when the object are destroyed, you can call those of method together and the GC will run immediately:

          System.runFinalization();
          System.gc();
      

      如果仅使用其中一种方法,GC将在稍后运行,并且可能会得到错误的结果.

      If you use only one of these methods, GC will run later, and you may get wrong results.

      推荐答案

      为每个Activity声明一个单独的模块根本不是一个好主意.为每个Activity声明单独的组件甚至更糟.其背后的原因非常简单-您实际上并不需要所有这些模块/组件(就像您自己已经看到的那样).

      Declaring a separate module for each Activity is not a good idea at all. Declaring separate component for each Activity is even worse. The reasoning behind this is very simple - you don't really need all these module/components (as you have already seen by yourself).

      但是,仅将一个组件绑定到Application的生命周期,并将其用于注入到所有Activities中也不是最佳的解决方案(这是您朋友的方法).这不是最佳选择,因为:

      However, having just one component that is tied to Application's life-cycle and using it for injection into all Activities is also not the optimal solution (this is your friend's approach). It is not optimal because:

      1. 它将您限制为一个范围(@Singleton或自定义范围)
      2. 您被限制的唯一作用域是使注入的对象成为应用程序单例",因此作用域的范围错误或使用不正确的作用域对象很容易导致全局内存泄漏
      3. 您也想使用Dagger2来注入Services,但是Services可能需要与Activities不同的对象(例如,Services不需要演示者,没有FragmentManager , 等等.).通过使用单个组件,您失去了为不同组件定义不同对象图的灵活性.
      1. It restricts you to just one scope (@Singleton or a custom one)
      2. The only scope you're restricted to makes the injected objects "application singletons", therefore mistakes in scoping or incorrect usage of scoped objects can easily cause global memory leaks
      3. You'll want to use Dagger2 in order to inject into Services too, but Services can require different objects than Activities (e.g. Services don't need presenters, don't have FragmentManager, etc.). By using a single component you loose the flexibility of defining different object graphs for different components.

      因此,每个Activity的组件是一个过大的功能,但是整个应用程序的单个组件不够灵活.最佳解决方案介于这两个极端之间(通常如此).

      So, a component per Activity is an overkill, but single component for the entire application is not flexible enough. The optimal solution is in between these extremes (as it usually is).

      我使用以下方法:

      1. 提供全局"对象的单个应用程序"组件(例如,拥有在应用程序中所有组件之间共享的全局状态的对象).在Application中实例化.
      2. 应用程序"组件的控制器"子组件,提供所有面向用户的控制器"所​​需的对象(在我的体系结构中为ActivitiesFragments).在每个ActivityFragment中实例化.
      3. 应用程序"组件的服务"子组件,提供所有Services所需的对象.在每个Service中实例化.
      1. Single "application" component that provides "global" objects (e.g. objects that hold global state which is shared between all components in the application). Instantiated in Application.
      2. "Controller" subcomponent of "application" component that provides objects which are required by all user-facing "controllers" (in my architecture these are Activities and Fragments). Instantiated in each Activity and Fragment.
      3. "Service" subcomponent of "application" component that provides objects which are required by all Services. Instantiated in each Service.

      以下是如何实现相同方法的示例.

      Following is an example of how you could implement the same approach.

      编辑2017年7月

      我发布了一个视频教程,展示了如何在Android应用程序中构建Dagger依赖项注入代码: Android Dagger for Professional教程.

      I published a video tutorial that shows how to structure Dagger dependency injection code in Android application: Android Dagger for Professionals Tutorial.

      编辑2018年2月

      我发布了关于依赖项注入的完整课程Android .

      在本课程中,我将解释依赖注入的理论,并展示它如何在Android应用程序中自然出现.然后,我演示Dagger构造如何适合一般的依赖项注入方案.

      In this course I explain the theory of dependency injection and show how it emerges naturally in Android application. Then I demonstrate how Dagger constructs fit into the general dependency injection scheme.

      如果您参加本课程,您将理解为什么为每个活动/片段分别定义模块/组件的定义在最根本的方式上存在缺陷.

      If you take this course you will understand why the idea of having a separate definition of module/component for each Activity/Fragment is basically flawed in the most fundamental way.

      这种方法导致功能"类集的表示层结构被镜像到构造"类集的结构中,从而将它们耦合在一起.这违背了依赖注入的主要目的,即保持类的构造"和功能"集不相交.

      Such an approach causes the structure of presentation layer from "Functional" set of classes to be mirrored into the structure of "Construction" set of classes, thus coupling them together. This goes against the main objective of dependency injection which is to keep the "Construction" and "Functional" sets of classes disjoint.

      应用范围:

      @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;
          }
      }
      

      有关依赖项注入的其他信息:

      Additional information on dependency injection:

      解密的匕首2望远镜

      Android中的依赖项注入

      这篇关于匕首-我们是否应该为每个活动/片段创建每个组件和模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆