DaggerAndroid 使用及原理

作者: SheHuan | 来源:发表于2019-01-21 12:44 被阅读23次

已经有了 Dagger 2 为什么还要使用 DaggerAndroid 呢?关于这一点在谷歌的官方说明文档:https://google.github.io/dagger//android.html 中已经有了明确的解释。DaggerAndroid 是谷歌基于 Dagger 2 的一个扩展库,更适合在 Android 开发中使用。为了更好的学习 DaggerAndroid,还不了解 Dagger 2 的同学建议先学习 Dagger 2:Dagger 2 使用及原理

一、更好的 DaggerAndroid

Dagger 2 虽然优秀,但在 Android 开发中确实会存在一些问题,回顾之前 Dagger 2 的使用,我们必须在 Activity 或 Fragment 的声明周期方法中添加类似这样的代码来完成依赖对象的创建和注入:

DaggerMainComponent.builder()
                .mainModule(new MainModule())
                .build()
                .inject(this);

咋一看可能觉得没什么问题,但如果在几十个甚至几百个 Activity 或 Fragment 中都添加类似的代码,这对后期的会维护、扩展还是会带来一定困难的;这些模板代码都大同小异,可能都是复制、粘贴,然后简单的修改下,这并不是一种好的做法,而且无法将其转移到基类里,我们更想看到的是每个 Activity 或 Fragment 中不出现类似的配置代码,即我不关心框架是如何给我注入依赖对象的,减少模板代码。

为了解决这样的问题,就有必要使用 DaggerAndroid。

二、基本使用

1、添加依赖库

implementation 'com.google.dagger:dagger-android:2.15'
// 如果使用了 Android support libraries 中的相关组件,则需要添加该依赖
implementation 'com.google.dagger:dagger-android-support:2.15'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.15'
// Dagger 2 的编译时注解处理器(必须)
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'

添加了依赖库后,接下来我们以 MainActivity 的依赖对象注入为例。

2、编写 Subcomponent + Module

首先编写 MainActivity 对应的 MainActivitySubcomponent 接口,注意泛型参数类型为要注入的的 MainActivity 类型,同时里边的 Builder 抽象类不可缺少:

@Subcomponent(modules = {AndroidInjectionModule.class})
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {

    }
}

创建 MainActivity 对应的 MainActivityModule 类,@Module注解的subcomponents属性值为前边创建的 MainActivitySubcomponent 类型,@ActivityKey注解的属性值为需要依赖注入的 MainActivity 类型,同时可以在 MainActivityModule 中定义提供依赖对象的方法,bindMainActivityInjectorFactory()方法的参数类型就是 MainActivitySubcomponent 内部 Builder 类:

@Module(subcomponents = {MainActivitySubcomponent.class})
public abstract class MainActivityModule {
    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);

    @Provides
    static FlowerBean provideFlower() {
        return new FlowerBean("rose", "red");
    }
}

3、编写全局的 Component

这个 AppComponent 会在自定义 Application 中用到,注意modules属性的参数,AndroidInjectionModule主要提供 DaggerAndroid 核心组件,必须配置,如果使用了 Android support libraries 则需要配置AndroidSupportInjectionModuleMainActivityModule则是我们自定义的,提供执行的依赖对象:

@Component(modules = {
        AndroidInjectionModule.class,
        AndroidSupportInjectionModule.class,
        MainActivityModule.class
})
public interface AppComponent {
    void inject(App app);
}

4、自定义 Application

这是关键的一步,自定义 Application 实现 HasActivityInjector 接口,实现activityInjector()接口,返回值为由框架负责注入的dispatchingActivityInjector对象,底层通过DaggerAppComponent.create().inject(this)会完成dispatchingActivityInjector的依赖注入,这个也是依赖注入的准备阶段,必不可少。注意DaggerAppComponent是项目编译后才会有的。

dispatchingActivityInjector是什么,有什么用呢?不看编译后生成的代码是不知道的,这个后边说。

public class App extends Application implements HasActivityInjector {
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

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

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

5、编写 MainActivity

声明需要依赖注入的 flower 对象,这个和 Dagger 2 是一样的,然后完成调用AndroidInjection.inject(this)会在底层完成对象的创建和注入。
注意AndroidInjection.inject(this)需要写在super.onCreate(savedInstanceState)之前。

public class MainActivity extends AppCompatActivity {
    @Inject
    FlowerBean flower;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.e("flower", flower.toString());
    }
}

在 Fragment 中的使用类似,有兴趣的可以参考 demo,链接会在文末给出,或参考官方的例子。

三、原理

上边我在结合官方示例的基础上,实现了依赖注入。但是整个过程有各种疑问,这个类有啥用,为啥这样写,它们之间有什么依赖关系。如果看编译时生成的代码,肯定一脸懵逼,只能照猫画虎的写代码,出现错误都不好找原因。如果不懂原理,真的很难灵活的运用它,这也是 Dagger 2 、DaggerAndroid 比较难上手的原因吧。

接下来我们来分析 DaggerAndroid 的依赖注入原理,看一看上边例子的各个部分是如何一起工作的。

先回顾一下 Dagger 2 的底层原理,当项目编译时会生成对应的辅助代码,然后通过类似文章开头的模板代码进一步的使用生成的辅助类完成依赖对象的创建和注入。

DaggerAndroid 同样也是在编译时生成对应的辅助代码,但之后需要先在自定义 Application 中通过DaggerAppComponent.create().inject(this)实现dispatchingActivityInjector的依赖注入,以保证activityInjector()方法的返回值有效,这个返回值是依赖注入的关键,这也算是依赖注入的准备阶段,因为当我们在 MainActivity 中执行AndroidInjection.inject(this)方法时需要间接的调用activityInjector()方法,最终来完成依赖对象的创建和注入,所以我们分两部分来看。

项目编译后生成的辅助代码如下:


1、准备阶段

首先看 Application 中的DaggerAppComponent.create().inject(this)是如何完成dispatchingActivityInjector的创建和注入。DaggerAppComponent生成类的源码直达:DaggerAppComponent

跟随create()方法逐步的看,首先是通过Builder内部类构建一个 DaggerAppComponent 对象:

public final class DaggerAppComponent implements AppComponent {
  private Provider<MainActivitySubcomponent.Builder> mainActivitySubcomponentBuilderProvider;

  private DaggerAppComponent(Builder builder) {
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static AppComponent create() {
    return new Builder().build();
  }

  public static final class Builder {
    private Builder() {}

    public AppComponent build() {
      return new DaggerAppComponent(this);
    }
  }
}

DaggerAppComponent 的私有构造函数中调用了initialize()为 DaggerAppComponent 的mainActivitySubcomponentBuilderProvider变成变量赋值:

private void initialize(final Builder builder) {
    this.mainActivitySubcomponentBuilderProvider =
        new Provider<MainActivitySubcomponent.Builder>() {
          @Override
          public MainActivitySubcomponent.Builder get() {
            return new MainActivitySubcomponentBuilder();
          }
        };
  }

mainActivitySubcomponentBuilderProviderProvider类型,通过get()方法可以返回其保存的MainActivitySubcomponentBuilder对象的值。继续看 MainActivitySubcomponentBuilder 的实现:

private final class MainActivitySubcomponentBuilder extends MainActivitySubcomponent.Builder {
    ......
    @Override
    public MainActivitySubcomponent build() {
      ......
      return new MainActivitySubcomponentImpl(this);
    }
    ......
  }

MainActivitySubcomponentBuilder 就是我们前边例子(二、2)中接口 MainActivitySubcomponent 内部 Builder 类的子类,重写了build()方法并返回了一个 MainActivitySubcomponentImpl 对象:

private final class MainActivitySubcomponentImpl implements MainActivitySubcomponent {
    private MainActivitySubcomponentImpl(MainActivitySubcomponentBuilder builder) {}

    @Override
    public void inject(MainActivity arg0) {
      injectMainActivity(arg0);
    }

    private MainActivity injectMainActivity(MainActivity instance) {
      MainActivity_MembersInjector.injectFlower(
             instance, MainActivityModule_ProvideFlowerFactory.proxyProvideFlower());
      return instance;
    }
  }

这个类实现了我们自定义的 MainActivitySubcomponent 接口,并重写了其父类AndroidInjectorinject()方法,在injectMainActivity()中,MainActivityModule_ProvideFlowerFactory.proxyProvideFlower()会创建一个 Flower 对象,MainActivity_MembersInjector.injectFlower()方法则会将创建好的 Flower 对象赋值给 MainActivity 中的flower变量,即依赖注入。

再回到前边的initialize()方法,所以mainActivitySubcomponentBuilderProvider中最终保存了一个MainActivitySubcomponentBuilder对象,通过该对象可以调用MainActivitySubcomponentImpl对象的inject()方法完成依赖对象的注入。

DaggerAppComponent.create()的核心内容就这些了,然后看其inject(this)方法:

public final class DaggerAppComponent implements AppComponent {

  @Override
  public void inject(App app) {
    injectApp(app);
  }

  private App injectApp(App instance) {
    App_MembersInjector.injectDispatchingActivityInjector(
        instance, getDispatchingAndroidInjectorOfActivity());
    return instance;
  }
}

DaggerAppComponent 实现了前边例子(二、3)中的 AppComponent 接口,并重写了inject()方法。其中核心方法就是 App_MembersInjectorinjectDispatchingActivityInjector()方法:

public static void injectDispatchingActivityInjector(
      App instance, DispatchingAndroidInjector<Activity> dispatchingActivityInjector) {
    instance.dispatchingActivityInjector = dispatchingActivityInjector;
  }

即给自定义 Application 中的 dispatchingActivityInjector成员变量赋值,那这个值是如何被创建的呢,我们看getDispatchingAndroidInjectorOfActivity()方法:

 private DispatchingAndroidInjector<Activity> getDispatchingAndroidInjectorOfActivity() {
    return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(
        getMapOfClassOfAndProviderOfFactoryOf());
  }

先看里边的getMapOfClassOfAndProviderOfFactoryOf()方法:

private Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
      getMapOfClassOfAndProviderOfFactoryOf() {
    return Collections
        .<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
            singletonMap(MainActivity.class, (Provider) mainActivitySubcomponentBuilderProvider);
  }

就是返回了一个 key 为MainActivity.class,value 为 mainActivitySubcomponentBuilderProvider的 Map,那么上边DispatchingAndroidInjector_Factory类的newDispatchingAndroidInjector()方法呢:

public static <T> DispatchingAndroidInjector<T> newDispatchingAndroidInjector(
      Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories) {
    return new DispatchingAndroidInjector<T>(injectorFactories);
  }

返回了一个DispatchingAndroidInjector类的对象,所以 DispatchingAndroidInjector 对象中就保存了需要依赖注入的 MainActivity 类型,以及可以实现 MainActivity 依赖注入的mainActivitySubcomponentBuilderProvider对象的Map,所以我们 Application 中的 dispatchingActivityInjector成员变量也就有了同样的信息。

到这里依赖注入的准备阶段就结束了。

2、依赖注入阶段

依赖注入阶段就是通过 MainActivity 中的AndroidInjection.inject(this)完成的,我们来看inject(this)的实现:

public static void inject(Activity activity) {
    checkNotNull(activity, "activity");
    Application application = activity.getApplication();
    if (!(application instanceof HasActivityInjector)) {
      throw new RuntimeException(
          String.format(
              "%s does not implement %s",
              application.getClass().getCanonicalName(),
              HasActivityInjector.class.getCanonicalName()));
    }

    AndroidInjector<Activity> activityInjector =
        ((HasActivityInjector) application).activityInjector();
    checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());

    activityInjector.inject(activity);
  }

即先得到当前 Activity 的 Application,并且 Application 需要实现HasActivityInjector接口,我们的自定义 Application 符合这样的条件。然后调用 Application 中重写的 activityInjector()方法,所以activityInjector.inject(activity)就是通过我们 Application 中的dispatchingActivityInjector对象的inject()方法,即DispatchingAndroidInjector类的inject()方法:

public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
  @Override
  public void inject(T instance) {
    boolean wasInjected = maybeInject(instance);
    if (!wasInjected) {
      throw new IllegalArgumentException(errorMessageSuggestions(instance));
    }
  }
}

间接调用了maybeInject()

public boolean maybeInject(T instance) {
    Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
        injectorFactories.get(instance.getClass());
    if (factoryProvider == null) {
      return false;
    }

    @SuppressWarnings("unchecked")
    AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
    try {
      AndroidInjector<T> injector =
          checkNotNull(
              factory.create(instance), "%s.create(I) should not return null.", factory.getClass());

      injector.inject(instance);
      return true;
    } catch (ClassCastException e) {
      throw new InvalidInjectorBindingException(
          String.format(
              "%s does not implement AndroidInjector.Factory<%s>",
              factory.getClass().getCanonicalName(), instance.getClass().getCanonicalName()),
          e);
    }
  }

先从准备阶段保存的Map中取出 MainActivity 对应的 mainActivitySubcomponentBuilderProvider对象,然后取出里边的值,即 MainActivitySubcomponentBuilder对象,它实现了AndroidInjector.Factory<T>接口,我们简化一下try-catch里边的代码:

 AndroidInjector<T> injector = factory.create(instance);
 injector.inject(instance);

factory.create(instance)最终会调用MainActivitySubcomponentBuilderbuild()方法来创建MainActivitySubcomponentImpl对象,所以injector就是一个MainActivitySubcomponentImpl对象,所以通过injector.inject(instance)就调用了前边准备阶段MainActivitySubcomponentImpl实现类中的inject(MainActivity arg0)方法,最终完成了 MainActivity 中依赖对象的创建和注入。

四、小结

DaggerAndroid 相比 Dagger 2 的底层依赖注入原理要复杂一些,你写的代码越少意味着框架要帮你做的事情越多。通过阅读源码,可以更好的理解我们使用框架的每一步,框架底层是如何工作的,而不是简单的使用。

但是不得不说,DaggerAndroid 的使用过程依旧比较繁琐,还是会让很多人望而却步的,如果你用 Kotlin 语言开发 Android 的话,那么有更好用、更简单的依赖注入框架可以选择,那就是 Kodein-DI

示例代码在 https://github.com/SheHuan/Dagger2Demo 的 dagger-android 分支上。

相关文章

网友评论

    本文标题:DaggerAndroid 使用及原理

    本文链接:https://www.haomeiwen.com/subject/mxvekqtx.html