Dagger2详解——最直白易懂、详细的dagger2注入流程原理

项目中有使用dagger2,自己一直处于一个只会套模板使用的状态,项目做完研究了下dagger2注入流程的原理,写一下自己的心得。不熟悉dagger2使用流程的同学可以先去熟悉下使用流程,这里不做具体探讨。

dagger2版本使用的是2.14.1,旧版本生成的代码可能会有所不同,但逻辑基本相同

一、注入流程

1.创建module,这里简单地创建了一个string对象,编译过后就会生成CommonModule_ProvideContentFactory类


图片.png

CommonModule_ProvideContentFactory类 实现了Factory, 泛型是provider返回的对象,构造方法中传入了module对象。其中get()方法调用了module中的provideContent,返回了这个方法的结果


图片.png

2.创建CommonComponent,构建后生成DaggerCommonComponent 以及Dagger2Activity_MembersInjector
图片.png

先看下Dagger2Activity_MembersInjector ,构造方法中传入了利用第一步生成的CommonModule_ProvideContentFactory封装的Provider对象contentProvider,在MembersInjector方法中调用了contentProvider.get(),最终就调用到了module中的方法,并把这个值赋值给需要注入的实例,实例就是Dagger2Activity

public final class Dagger2Activity_MembersInjector implements MembersInjector<Dagger2Activity> {
  private final Provider<String> contentProvider;

   //构造方法中传入contentProvider
  public Dagger2Activity_MembersInjector(Provider<String> contentProvider) {
    this.contentProvider = contentProvider;
  }

  public static MembersInjector<Dagger2Activity> create(Provider<String> contentProvider) {
    return new Dagger2Activity_MembersInjector(contentProvider);
  }

  
  @Override
  public void injectMembers(Dagger2Activity instance) {
    injectContent(instance, contentProvider.get());//contentProvider.get()获取到module实例对象中提供的具体值
  }
 //赋值给注入对象的成员变量
  public static void injectContent(Dagger2Activity instance, String content) {
    instance.content = content;
  }
}
接下来看DaggerCommonComponent和Dagger2Activity

Dagger2Activity ,这里箭头注入了一个string对象,初始化时调用了DaggerCommonComponent的inject方法,调用这个方法后就完成了注入


图片.png

DaggerCommonComponent ,这是注入过程中的一个桥梁。内部定义了一个Builder对象,Builder对象里有我们在CommonComponent里定义的module成员变量,activity调用commonModule对module赋值。
赋值完就调用build返回DaggerCommonComponent实例化对象。DaggerCommonComponent的构造方法里,利用Builder里的module对象 创建第一步里的provider对象 。
再调用DaggerCommonComponent的inject方法,inject方法里调用了Dagger2Activity_MembersInjector 里的静态方法injectContent,对Dagger2Activity里的content赋值,content的值来自provideContentProvider.get(),到这里赋值就完成了

public final class DaggerCommonComponent implements CommonComponent {
  private Provider<String> provideContentProvider;

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

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

  private Dagger2Presenter getDagger2Presenter() {
    return new Dagger2Presenter(provideViewProvider.get());
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
  
    this.provideContentProvider =
        DoubleCheck.provider(CommonModule_ProvideContentFactory.create(builder.commonModule));
  }

  @Override
  public void inject(Dagger2Activity activity) {
    injectDagger2Activity(activity);
  }

  private Dagger2Activity injectDagger2Activity(Dagger2Activity instance) {
    Dagger2Activity_MembersInjector.injectContent(instance, provideContentProvider.get());
    return instance;
  }

  public static final class Builder {
    private CommonModule commonModule;//在CommonComponent里定义的module

    private Builder() {}

    //返回DaggerCommonComponent实例化对象
    public CommonComponent build() {
      if (commonModule == null) {
        throw new IllegalStateException(CommonModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerCommonComponent(this);
    }
    //activity里调用这个方法对module进行赋值
    public Builder commonModule(CommonModule commonModule) {
      this.commonModule = Preconditions.checkNotNull(commonModule);
      return this;
    }
  }
}

关于DoubleCheck

provider(Provider<T> delegate)调用了构造器方法,构造器中将传入的Provider对象保存起来了,调用get()时会调用保存的provider对象的get(),实际上就是调用工厂方法的get()拿到对象,这样就实现了懒加载,在需要的时候调用get(),这时才会调用工厂方法的get(),因为真正创建对象的细节是封装在工厂类的get()中的,同时,它会将得到的对象缓存起来,这样下次调用就不需要再调用工厂类创建对象了。

public static <T> Provider<T> provider(Provider<T> delegate) {
...    
    return new DoubleCheck<T>(delegate);
}
  
  private DoubleCheck(Provider<T> provider) {
    assert provider != null;
    this.provider = provider;
  }

  @SuppressWarnings("unchecked") // cast only happens when result comes from the provider
  @Override
  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          /* Get the current instance and test to see if the call to provider.get() has resulted
           * in a recursive call.  If it returns the same instance, we'll allow it, but if the
           * instances differ, throw. */
          Object currentInstance = instance;
          if (currentInstance != UNINITIALIZED && currentInstance != result) {
            throw new IllegalStateException("Scoped provider was invoked recursively returning "
                + "different results: " + currentInstance + " & " + result + ". This is likely "
                + "due to a circular dependency.");
          }
          instance = result;
          /* Null out the reference to the provider. We are never going to need it again, so we
           * can make it eligible for GC. */
          provider = null;
        }
      }
    }
    return (T) result;
  }

流程总结

首先创建好module 和Component 类,以及需要注入的activity,编译后生成相关的类
在activity初始化中调用注入方法,从这行代码开始分析流程

DaggerCommonComponent.builder().commonModule(new CommonModule(this)).build().inject(this);
1.DaggerCommonComponent.builder()获取到Builder对象
2.commonModule(new CommonModule(this)),给Builder力的module成员变量赋值
3.build(),创建了DaggerCommonComponent对象,DaggerCommonComponent的构造方法中利用Builder
里的module对象 创建里的provider对象,provider构造参数是实例化的module,其中get()方法返回module里@provider标记的方法返回值

4.inject(this),调用Dagger2Activity_MembersInjector里的inject方法,inject方法参数一是activity,
参数二是provider.get()获取到的具体值,然后赋值给activity里@inject标记的成员变量

二、关于注入有三种方式

构造方法注入:在类的构造方法前面注释@Inject
成员变量注入:在类的成员变量(非私有)前面注释@Inject
函数方法注入:在函数前面注释@Inject

上面例子说明的是,在类的成员变量(非私有)前面注释@Inject

1.接下去看下在类的构造方法前面注释@Inject
创建present类,需要传入一个view对象,在构造方法注释@Inject


图片.png

dagger2activity中多了一行注入代码


图片.png

module第一步上面已经写好了,编译后生成CommonModule_ProvideViewFactory
Dagger2Activity_MembersInjector中增加了present相关的注入方法,与string 的注入方法一样
图片.png

DaggerCommonComponent中多了present的注入方法,注意在injectDagger2Activity里注入present时调用了getDagger2Presenter,而这个方法里new了一个Dagger2Presenter,参数传的是从provider中获得的view,其他步骤和成员变量注入一样


图片.png

2.在函数前面注释@Inject
dagger2activity中修改,在一个方法中注释@Inject,并把注入的值赋值给content
图片.png

Dagger2Activity_MembersInjector 中箭头处的赋值方法由变量赋值变成了方法调用,其他逻辑不变
图片.png

DaggerCommonComponent里的逻辑没有变化

总结

在类的成员变量(非私有)前面注释@Inject 和在函数前面注释@Inject ,注入的逻辑流程一样,只是赋值时一个是成员变量赋值,一个是调用方法
在类的构造方法前面注释@Inject,Component会增加一个方法new 出这个对象

这里说明一下,我dagger2版本使用的是2.14.1。而在2.11版本里,new出present对象的方法是在provider里,Component里使用的是Dagger2Activity_MembersInjector.injectMembers方法,而不是具体的静态方法,其他逻辑一样

参考文献 https://www.jianshu.com/p/eef7fa8136e7

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350