使用DispatchingAndroidInjector<>和其他dagger.android类有哪些优点?

12

我正在将Dagger 2集成到我的Android项目中。这是我第一次使用这个框架,目前一切顺利。但是,在设置框架时我看到了不同的方法,我想知道哪种更好,因为我对比了两种方法,对我来说结果差不多。

我遵循了这个指南:https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2

在网上搜索,所有人都使用这种方法。 它使用@Module和@Component来定义依赖关系。

你的应用程序最终会变成这样:

public class MyApp extends Application {

    private NetComponent mNetComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        // Dagger%COMPONENT_NAME%
        mNetComponent = DaggerNetComponent.builder()
                // list of modules that are part of this component need to be created here too
                .appModule(new AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
                .netModule(new NetModule("https://api.github.com"))
                .build();

        // If a Dagger 2 component does not have any constructor arguments for any of its modules,
        // then we can use .create() as a shortcut instead:
        //  mNetComponent = com.codepath.dagger.components.DaggerNetComponent.create();
    }

    public NetComponent getNetComponent() {
       return mNetComponent;
    }
}

但我发现另一种方法(我没有测试过):https://google.github.io/dagger/android.html 看起来完全不同,使用了不同的类和注解。 它使用了类似这样的东西:

@Subcomponent(modules = ...)
 public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
   @Subcomponent.Builder
   public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
 }

@Module(subcomponents = YourActivitySubcomponent.class)
 abstract class YourActivityModule {
   @Binds
   @IntoMap
   @ActivityKey(YourActivity.class)
   abstract AndroidInjector.Factory<? extends Activity>
       bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
 }

 @Component(modules = {..., YourActivityModule.class})
 interface YourApplicationComponent {}

public class YourApplication extends Application implements HasDispatchingActivityInjector {
   @Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

   @Override
   public void onCreate() {
     super.onCreate();
     DaggerYourApplicationComponent.create()
         .inject(this);
   }

   @Override
   public DispatchingAndroidInjector<Activity> activityInjector() {
     return dispatchingActivityInjector;
   }
 }

所以,我的问题是:

  1. 哪个更好?

  2. 选择一种方法而不是另一种方法的原因是什么?


你能详细说明有什么不同吗? - Shmuel
另外,看一下这个 https://github.com/googlesamples/android-architecture/tree/todo-mvp-dagger/ 通常我会遵循谷歌推荐的任何建议。他们维护Dagger 2 :D - Shmuel
我附上了代码中的主要差异,以便更好地理解我的意思! - Leandro Ocampo
@Shmuel,你给出的示例代码与我编写的代码相同。但是这个链接(https://google.github.io/dagger/android.html)显示的内容有所不同。正如你所看到的,这个URL也来自谷歌 =P。也许我漏掉了什么。 - Leandro Ocampo
5
您会发现Dagger的实现方式有很多种,它们的复杂程度也各不相同。最后一个例子似乎是通过泛型化注入过程并使用子组件来实现的 - 老实说,我尝试使用KISS原则。考虑到这一点,哪种方法适合您的目的并同时实现相同的目标。对我而言,使用此库解决的依赖注入问题正在被越来越复杂的实现所抵消。 - Mark
1个回答

22

现在官方的Dagger 2文档中建议采用的Android Dagger 2设置方法有许多优点,应该优先考虑使用。这些优点正是文档中所阐述的:

  1. 复制粘贴代码会使以后进行重构变得困难。随着越来越多的开发人员复制粘贴该块代码,越来越少的人会知道它实际上是做什么的。

  2. 更根本的是,它要求请求注入的类型(FrombulationActivity)知道它的注入器。即使通过接口而不是具体类型完成,它也违反了依赖注入的核心原则:一个类不应该知道它是如何被注入的。

让我们将这些原因应用于您的第一个示例。

原因1

假设我们有一个希望使用您的NetComponent的Activity。我们称之为NetActivity。那个NetActivityonCreate(Bundle savedInstanceState)方法将看起来像这样:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ((MyApp) getApplicationContext()).getNetComponent().inject(this);
}

这段代码的视觉吸引力就像是散落在燕麦片上的趾甲碎片(不是我编造的比喻),最终会被复制粘贴到所有使用 NetComponent 的注入站活动中。如果您使用更复杂的组件,例如文档中的示例:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  // DO THIS FIRST. Otherwise frombulator might be null!
  ((SomeApplicationBaseType) getContext().getApplicationContext())
     .getApplicationComponent()
     .newActivityComponentBuilder()
     .activity(this)
     .build()
     .inject(this);
 // ... now you can write the exciting code

}

更糟糕的是,它很容易退化成一个神奇的代码片段,在注入点必须复制和粘贴。如果它发生了改变,很容易忘记更新其中一个站点,从而导致应用程序崩溃。 原因2 依赖注入的一个巨大优势是注入点无需知道或关心它们的注入器,就像依赖项不知道或关心其相关方一样。回到我们的NetActivity
((MyApp) getApplicationContext()).getNetComponent().inject(this);

Activity“知道”它的注入器(NetComponent),并且Activity现在与具体实现MyApp和同一方法getNetComponent()耦合。如果这些类中的任何一个发生更改,NetActivity也必须相应更改。

遵循Dagger 2.10及其以上版本中Activity和Fragment内部注入的新方式的优点正好与这些缺点相反:

  1. 您最终将减少复制粘贴的代码
  2. 请求注入的类型不再需要知道或关心它们的注入器或注入器的来源。

此外,正如this blog所指出的那样,首选子组件而不是依赖组件可以减少应用程序的方法计数。

虽然使用子组件可能初看起来更难,但有一些明显的优点。然而,为了学习Dagger,依赖组件可能更容易理解。如果第二个示例初始时过于复杂,则可以在掌握技巧后转向首选方法。


嗨,大卫。我看到了页面上描述的那些优点。虽然第二个优点有道理,但第一个优点并不正确:“复制粘贴代码会使以后重构变得困难。随着越来越多的开发人员复制粘贴该块,越来越少的人会知道它实际上是做什么的。”最终你还是要将这段代码“((MyApp) getApplication()).getNetComponent().inject(this);”更改为“AndroidInjection.inject(this);”,所以你仍然需要粘贴代码。 - Leandro Ocampo
@LeandroOcampo 你说得对,仍然会有一些复制/粘贴。但是采用第二种方法,就不会那么多了。无论如何,感谢您的接受。 - David Rawson
5
我认为不同之处在于人们可能可以推断出 AndroidInjection.inject(this) 的作用,因为它很简单明了。而在前一个示例中以及文档中的 // DO THIS FIRST 示例中,代码要复杂得多,需要更多对代码功能的理解。当事情变得更加复杂时,那些只是简单地复制粘贴代码的人可能甚至不会试图去理解它,他们只是复制它,因为“这是你应该做的”。Injector.inject(this); 就像 super.onCreate() 一样容易理解,我们已经毫不犹豫地使用它了。 - Joe
@Joe,这是一个很好的观点。你想把它变成一个答案吗? - David Rawson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接