Dagger & Android (中文翻译)

1,017 阅读6分钟

Dagger2相比于其他大多数依赖注入框架的主要优势就是它是严格是实现(没有通过反射),那意味着它可以用在Android应用中。然而,在Android应用中使用Dagger还是有一些需要考虑的地方。

理念

虽然为Android编写的代买是Java源码,他在风格上常常有很大的不同。通常,存在这样的差异是出狱移动平台的独特性能考虑。

但是通畅应用于Android代码的许多模式与应用于其他Java代码的模式相反。在Effect Java中的许多建议也被认为不适合Android。

为了实现惯用代码的习惯性用法和可移植性,Dagger依赖proGuard来对编译后的字节码进行后处理。这使得Dagger可以生成在服务器和Android上看起来很自然的源码,同时使用不同的工具链生成在两种环境都能高效执行的字节码。再者,Dagger有一个明确的目标就是确保其生成的Java源代码始终与ProGuard优化兼容。

当然,并不是所有的问题都能以上述的那种方式解决,但这是提供Android特定兼容性的主要机制。

tl;dr

Dagger假设Android用户将使用ProGuard

为什么在Android中使用Dagger很难

使用Dagger编写Android应用程序的主要困难之一是操作系统实例化了许多Android框架类,像是Activity和Fragment,但是Dagger若果可以创建所有注入的对象,则效果最好。相反,必须在生命周期方法中执行成员参数注入,这意味着许多类最终看起来像是如下:

public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

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

这样存在一些问题:

1.复制粘贴代码使得以后很难重构。随着越来越多的开发者使用复制张贴块(这不是在说我吗,捂脸),知道它实际功能的人就越来越少了。

2.更重要的是,他要求请求注入的类型(FrombulationActivity)知道他的注入器,即使通过接口而不是具体类型完成此操作,他也会破坏依赖注入的核心原理:一个类不应该知道它是如何注入的。

dagger.android

dagger.android中的classes提供一种方法来简化上述问题.这需要学习一些额外的API和概念,给你提供了简化的样板和注入方式,在你的Android类中。

另一个种方法是只是用常规的Dagger API,并遵循此处的指南,这可能更容易理解但缺点是需要手动编写额外的样板。

Jetpack和Dagger团队正在合作开发一款基于Android的Dagger新项目,希望能在很大程度上改变现状.不幸的是目前还没有完成,如何在当下选择在你的Android项目中使用Dagger时,可能需要考虑这一点.

注入Activity对象

1.在你的Application component中安装AndroidInjectionModule来确保这些基本类型所需要的所有绑定都可用。

2.首先用@Subcomponent注解注释一个实现AndroidInjector的YourActivitySubcomponent接口,用@Subcompont.Factory注解注解继承了AndroidInjector.Factory的Factory内部接口:

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

3.用@Module注解注解一个YourActivityModule,绑定了subcomponent Factory,subcomponents指向上面所写的YourActivitySubComponent,将其添加到YourApplicationComponent注入你的Application中:

@Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
  @Binds
  @IntoMap
  @ClassKey(YourActivity.class)
  abstract AndroidInjector.Factory<?>
      bindYourAndroidInjectorFactory(YourActivitySubcomponent.Factory factory);
}

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

小提示:如果你的subcomponent和其Factory没有有第2步中提到的方法或者超类型,你可以使用@ContributesAndroidInjcetor来生成它们。而不是步骤2和3中,通过抽象的module方法 给你的Activity,用@ContributesAndroidInjector注视她,并在你想要的安装的Subcomponent中指定module,如果Subcomponent需要作用域,使用@Scope注解注视到方法上。

@ActivityScope
@ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourAndroidInjector();

4.接下来,让你的Application实现HasAndroidInjector,使用@Inject注解androidInjector()返回的DispatchingAnroidInjector对象

public class YourApplication extends Application implements HasAndroidInjector {
  @Inject DispatchingAndroidInjector<Object> dispatchingAndroidInjector;

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

  @Override
  public AndroidInjector<Object> androidInjector() {
    return dispatchingAndroidInjector;
  }
}

5.终于,在你的Activity.onCreate()方法中,在调用super.onCreate()之前调用AndroidInjection.inject(this);:

public class YourActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
  }
}

原理?

AndroidInjection.inject()从Application得到DispatchingAndroidInjector注入到你的Activity中,DispatchingAndroidInjector查找你的Activity中的类对应AndroidInjector.Factory(就是YourActivitySubcomponent.Factory),创建AndroidInjector,通过injcect()注入到你的Activity中。

注入Fragment对象

在Fragment中注入对象和在Activity中一样简单,以同样的方式定义你的Subcomponent。

不同于在Activity的onCreat()中注入参数,Fragment是在onAttach()中。

与Activity定义Module不同,你可以选择在任何地方制定Fragment的Module.你可以使你的Fragmet对应的Component是另一个Fragment对应Component的Subcomponent,一个Activity对应的Component,或者是Application对应的Component,这完全去解决你的Fragment需要哪些依赖。在确定好Component的位置之后,使相应的类实现HasAndroidInjector(若果没有准备)。例如,如果你的Fragment需要绑定YourActivitySubcomponent,你的代买将看起来如下:

public class YourActivity extends Activity
    implements HasAndroidInjector {
  @Inject DispatchingAndroidInjector<Object> androidInjector;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
    // ...
  }

  @Override
  public AndroidInjector<Object> androidInjector() {
    return androidInjector;
  }
}

public class YourFragment extends Fragment {
  @Inject SomeDependency someDep;

  @Override
  public void onAttach(Activity activity) {
    AndroidInjection.inject(this);
    super.onAttach(activity);
    // ...
  }
}

@Subcomponent(modules = ...)
public interface YourFragmentSubcomponent extends AndroidInjector<YourFragment> {
  @Subcomponent.Factory
  public interface Factory extends AndroidInjector.Factory<YourFragment> {}
}

@Module(subcomponents = YourFragmentSubcomponent.class)
abstract class YourFragmentModule {
  @Binds
  @IntoMap
  @ClassKey(YourFragment.class)
  abstract AndroidInjector.Factory<?>
      bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Factory factory);
}

@Subcomponent(modules = { YourFragmentModule.class, ... }
public interface YourActivityOrYourApplicationComponent { ... }

基本框架类型

因为DispatchingAndroidInjector在运行时按类查找相应的AndroidInjector.Faactory,所以一个积累可以实现HasAndroidInjector并调用AndroidInjection.inject().每一个子类需要做的就是绑定一个对应的@Subcomponent,Dagger提供了几个这样的基本类型,例如DaggerActivityDaggerFragmetn.如果你没有有负责的层级结构,Dagger也提供了一个用于相同目的的DaggerApplication--你需要做的就是继承他并重写applicationInjector(),并返回应该注入到Application中的Component.

还包括一下类型:

注意DaggerBroadcastReceiver仅应该在AndroidManifest.xml注册BroadcastReceive时使用。使用你自己的代码创建BroadcastReceive时,请改用构造函数注入。

Support libraries

dagger.android.support package中给用户提供Android support libary,并行类型。

TODO(ronshapiro):我们应该开始通过androidx包对此进行拆分

我怎么引用Dagger2

在你的build.gragle中添加一下内容:

dependencies {
  compile 'com.google.dagger:dagger-android:2.x'
  compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
  annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
}

参考资料

-Dagger文档

备注:

翻译水平有限,此文有任何可以错误或者需要改进的地方,请在评论区指出,我会修改。