美文网首页
Dagger and Android

Dagger and Android

作者: xinguoshui | 来源:发表于2019-11-01 15:23 被阅读0次

Dagger and Android

  • @Component tells Dagger to implement an interface or abstract class that creates and returns one or more application objects.
    • Dagger will generate a class that implements the component type. The generated type will be named DaggerYourType (or DaggerYourType_NestedType for nested types)
  • @Inject on a constructor tells Dagger how to instantiate that class. We’ll see more shortly.
  • @Modules are classes or interfaces that act as collections of instructions for Dagger on how to construct dependencies. They’re called modules because they are modular: you can mix and match modules in different applications and contexts.
  • @Binds methods are one way to tell Dagger how to construct an instance. They are abstract methods on modules that associate one type that Dagger already knows how to construct (the method’s parameter) with a type that Dagger doesn’t yet know how to construct (the method’s return type).

module可以用于面向接口编程,compoment就是dagger的root. 对于Android中Application/Activity/Fragment这些系统构造的对象,不能用@Inject修饰其构造方法,就需要在component中设置inject()方法来完成注入。

AndroidInjector/DispatchingAndroidInjector究竟做了什么?

DaggerApplication有两个方法:

  1. applicationInjector(),然后在DaggerApplication的onCreate方法中:
    @Override
    public void onMAMCreate() {
        super.onMAMCreate();
        injectIfNecessary();
    }
    
    这样就完成了对application的injection.
  2. androidInjector(), 可以自定义BaseActivity/BaseService/...,然后在onCreate方法中进行注入:
    // BaseService
    @Override
    public void onCreate() {
        super.onCreate();
        Application application = getApplication();
        AndroidInjector<Object> injector = ((HasAndroidInjector) application).androidInjector();
        injector.inject(this);
    }
    

Application中的androidInjector()会调用Component的androidInjector()来完成实际的注入工作。
Component的androidInjector实际上是一个DispatchingAndroidInjector, DispatchingAndroidInjector里面包含了所有要inject的对象的list。

    public DispatchingAndroidInjector<Object> androidInjector() {
      return DispatchingAndroidInjector_Factory.newInstance(getMapOfClassOfAndProviderOfAndroidInjectorFactoryOf(), ImmutableMap.<String, Provider<AndroidInjector.Factory<?>>>of());}

Component的DispatchingAndroidInjector是怎么生成的呢?
Answer: Dagger在生成图的时候会找到所有在Component中声明的有injection需求的Activity/Fragments等(或者是在Module中用@ContributesAndroidInjector来声明的,参考文档。):

// DaggerApplicationComponent.java
    private Map<Class<?>, Provider<AndroidInjector.Factory<?>>> getMapOfClassOfAndProviderOfAndroidInjectorFactoryOf(
        ) {
      return ImmutableMap.<Class<?>, Provider<AndroidInjector.Factory<?>>>builderWithExpectedSize(315).put(...)}

官方文档

contributesAndroidInjector是干嘛用的?Dagger是怎么利用这个annotation的?
Dagger利用contributesAndroidInjector annotation来为相应的Activity/Fragment生成AndroidInjector.Factory,
然后才能对Activity做Inject。
https://dagger.dev/android

How did that work?
AndroidInjection.inject() gets a DispatchingAndroidInjector<Object> from the Application and passes your activity to inject(Activity). The DispatchingAndroidInjector looks up the AndroidInjector.Factory for your activity’s class (which is YourActivitySubcomponent.Factory), creates the AndroidInjector (which is YourActivitySubcomponent), and passes your activity to inject(YourActivity).

DispatchingAndroidInjector是由谁生成的?Dagger Android对DispatchingAndroidInjector做了内部处理?
是由Application的applicationInjector()方法生成的, BaseApplicationComponent继承自了AndroidInjector

DaggerActivity_MembersInjector的injectMembers是怎么工作的?在activity的哪个生命周期地方进行inject的?
答案: 是在DaggerActivity的onCreate()方法的AndroidInjection.inject(this);来调用DaggerActivity_MembersInjector的injectMembers方法。DaggerActivity的androidInjector也是被AndroidInjection.inject(this)方法inject进去的。

// DaggerApplicationComponent.java
DaggerActivity_MembersInjector.injectAndroidInjector(instance, DaggerApplicationComponent.this.getDispatchingAndroidInjectorOfObject());

AboutActivity的androidInjector是DaggerApplicationComponent的DispatchingAndroidInjector,里面不包含DataContextComponent这个sub component里面的模块。
MainActivity的androidInjector是DataContextComponent的DispatchingAndroidInjector,里面包含了DataContextComponent里面的模块。

这种no injector发exception为什么不能在编译时发现呢?

John说的Injector会在sign in之后变是真的吗?然后injector根据scope(UserScope/Singleton)来觉得某个component能不能访问?
确实是对的,DaggerActivity会用AndroidInjection.inject(Activity),实现如下:

  /**
   * Injects {@code activity} if an associated {@link AndroidInjector} implementation can be found,
   * otherwise throws an {@link IllegalArgumentException}.
   *
   * @throws RuntimeException if the {@link Application} doesn't implement {@link
   *     HasAndroidInjector}.
   */
  public static void inject(Activity activity) {
    checkNotNull(activity, "activity");
    Application application = activity.getApplication();
    if (!(application instanceof HasAndroidInjector)) {
      throw new RuntimeException(
          String.format(
              "%s does not implement %s",
              application.getClass().getCanonicalName(),
              HasAndroidInjector.class.getCanonicalName()));
    }

    inject(activity, (HasAndroidInjector) application);
  }

  private static void inject(Object target, HasAndroidInjector hasAndroidInjector) {
    AndroidInjector<Object> androidInjector = hasAndroidInjector.androidInjector();
    checkNotNull(
        androidInjector, "%s.androidInjector() returned null", hasAndroidInjector.getClass());

    androidInjector.inject(target);
  }

最终会调用SkypeTeamsApplication的androidInjector()方法。SkypeTeamsApplication的androidInjector()方法,会根据用户有没有登录,来决定是返回ApplicationComponent的injector,还是返回DataContextComponent的injector。但是呢,因为AboutActivity的Scope是Singleton,而ConversationDao等data object的Scope是UserScope。所以呢,DataContextComponent的对于AboutActivity的Provider,其实和ApplicationComponent的对于AboutActivity的Provider一模一样,依然不会提供ConversationDao的Provider,然后呢,在DataContextComponent的injector对AboutActivity做injection的时候,会把AboutActivity的androidInjector设置成ApplicationComponent的injector,所以就会出现No injector factory bound的exception。
所以对于登录的用户,AboutActivity的流程是:

  1. DaggerActivity调用AndroidInjection.inject(this);,会得到DataContextComponent的injector来inject AboutActivity.
  2. 在做inject的时候,AboutActivity的androidInjector会被设置成ApplicationComponent的injector,因为编译时就确定了AboutActivity的scope和ApplicationComponent的scope一样。
  3. 如果我们在AboutActivity启动Cortana,就会crash,因为AboutActivity的androidInjector(其实是DispatchingAndroidInjector)的map里面是没有data(ConverstionDao/CortanaViewModel/...)相关的的provider的,自然就会crash。

为什么我的tricky fix就能解决问题呢?
因为我强行让CortanaViewModel和CortanaActionExecutor去获取SkypeTeamsApplication的injector,然后因为用户已经登录了,SkypeTeamsApplication的androidInjector()方法会返回DataContextComponent的DispatchingAndroidInjector,所以自然就可以对CortanaViewModel和CortanaActionExecutor进行inject了。

有没有更好的解决办法?在attach Cortana的时候,把当前Activity的injector替换掉?

相关文章

网友评论

      本文标题:Dagger and Android

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