Dagger2 学习与理解

Dagger2 使用编译时注解 + APT 实现依赖注入,能带来什么好处呢?一个词:解耦。在项目越来越庞大的时候,Dagger2 就体现出强大之处了。

编译时注解 + APT 实现的框架,通过解析注解,然后生成辅助类。But,是没用到反射的。都是在编译时完成。要分析 Dagger2 的工作原理,那么就要去探索生成的辅助类。
一个简单的例子,往 MainActivity 注入 MainPresenter 依赖(ps:MVP + Dagger2 简直不能太般配):

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    @Inject
    MainPresenter presenter;

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

        injectDependences();
        Log.d(TAG,"MainPresenter ->" + presenter.hashCode());
    }

    private void injectDependences(){
        DaggerMainComponent.builder()
                .mainModule(new MainModule())
                .build()
                .inject(this);
    }

}

@Component(modules = {MainModule.class})
public interface MainComponent {

    void inject(MainActivity mainActivity);

}

@Module
public class MainModule {

    @Provides MainPresenter provideMainPresenter(){
        return new MainPresenter();
    }
}

public class MainPresenter {

}

以上代码直接贴了,很简单。接下来我们分析在编译时生成的类。这些类存放在在 app/build/generated/source/apt/debug/包名
DaggerMainComponent 源码如下所示:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerMainComponent implements MainComponent {
 private Provider<MainPresenter> provideMainPresenterProvider;
 private MembersInjector<MainActivity> mainActivityMembersInjector;

 private DaggerMainComponent(Builder builder) {  
   assert builder != null;
   initialize(builder);
 }

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

 public static MainComponent create() {  
   return builder().build();
 }

 private void initialize(final Builder builder) {  
   this.provideMainPresenterProvider = MainModule_ProvideMainPresenterFactory.create(builder.mainModule);
   this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideMainPresenterProvider);
 }


 //inject 方法实现依赖注入
 @Override
 public void inject(MainActivity mainActivity) {  
   mainActivityMembersInjector.injectMembers(mainActivity);
 }

 public static final class Builder {
   private MainModule mainModule;

   private Builder() {  
   }

   public MainComponent build() {  
     if (mainModule == null) {
       this.mainModule = new MainModule();
     }
     return new DaggerMainComponent(this);
   }

   public Builder mainModule(MainModule mainModule) {  
     if (mainModule == null) {
       throw new NullPointerException("mainModule");
     }
     this.mainModule = mainModule;
     return this;
   }
 }
}

DaggerMainComponent 是使用建造者模式创建的,那么先看到其内部的 Builder 类,Builder 类有一个 mainModule 方法,传入 MainModule 对象,如果 MainModule 为空会抛出空指针异常,最后 mainModule 方法返回了 this。

然后调用 build 方法。在 build 方法中创建一个 DaggerMainComponent 并返回,此时只需要关注 DaggerMainComponent 的构造方法即可。

在 DaggerMainComponent 的构造方法中,将 Builder 对象传入 initialize 方法,initialize 方法虽然只有两行代码,但这两行代码很关键。我们看到这个 initialize 方法做了两件事。

调用了 MainModule_ProvideMainPresenterFactory 类的静态方法 create,并将 MainModule 对象传入,返回了一个 Provider 对象。 这里的 MainModule 对象来自 Builder 对象。是在初始化 DaggerMainComponent 的时候创建的。

调用 MainActivity_MembersInjector 的 create,并将 Provider 对象传进。返回一个 MembersInjector 对象。

到这里,只需要记住。Apt 在编译时为我们生成了三个类
DaggerMainComponent
MainModule_ProvideMainPresenterFactory
MainActivity_MembersInjector

虽然名字很长,但是认真看几遍的话,很还记的。
以上是 DaggerMainComponent 类,
还需要继续挖掘 MainModule_ProvideMainPresenterFactory 和 MainActivity_MembersInjector。

MainModule_ProvideMainPresenterFactory 源码如下:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class MainModule_ProvideMainPresenterFactory implements Factory<MainPresenter> {
  private final MainModule module;

  public MainModule_ProvideMainPresenterFactory(MainModule module) {  
    assert module != null;
    this.module = module;
  }

  @Override
  public MainPresenter get() {  
    MainPresenter provided = module.provideMainPresenter();
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<MainPresenter> create(MainModule module) {  
    return new MainModule_ProvideMainPresenterFactory(module);
  }
}

我们在 DaggerMainComponent 中调用了 MainModule_ProvideMainPresenterFactory 类的静态方法 create。那么从调用入口开始分析,找到 create 方法。create 方法内部 new 了一个 MainModule_ProvideMainPresenterFactory 对象。注意到还有一个 get 方法,但是我们现在还不知道在哪里调用了这个 get 方法。先记下。
MainActivity_MembersInjector 源码如下:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final MembersInjector<AppCompatActivity> supertypeInjector;
  private final Provider<MainPresenter> presenterProvider;

  public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<MainPresenter> presenterProvider) {  
    assert supertypeInjector != null;
    this.supertypeInjector = supertypeInjector;
    assert presenterProvider != null;
    this.presenterProvider = presenterProvider;
  }

  @Override
  public void injectMembers(MainActivity instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.presenter = presenterProvider.get();
  }

  public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<MainPresenter> presenterProvider) {  
      return new MainActivity_MembersInjector(supertypeInjector, presenterProvider);
  }
}

同样是从调用入口 create 方法开始。生成 MainActivity_MembersInjector 对象。
到这里为止,初始化工作全部完成。
现在想一下 MainPresenter 是怎样注入到 MainActivity 的呢?

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

很容易猜想到是 DaggerMainComponent 的 inject 方法。
我们回到 DaggerMainComponent 从入口 inject 方法开始, inject 方法传进了MainActivity。然后调用了 MainActivityMembersInjector 对象的 injectMembers 方法,如下所示:

@Override
public void injectMembers(MainActivity instance) {  
  if (instance == null) {
    throw new NullPointerException("Cannot inject members into a null reference");
  }
  supertypeInjector.injectMembers(instance);
  instance.presenter = presenterProvider.get();
}

而在 injectMembers 方法,终于可以看到调用 get 方法的代码

instance.presenter = presenterProvider.get();

就这样完成了依赖注入,instance 是 MainActivity,而 presenter 是需要注入的 MainPresenter 对象。
那么现在只需要关注 presenterProvider 的 get 方法即可。presenterProvider 是什么? 是 MainModule_ProvideMainPresenterFactory 对象,在创建 MainActivity_MembersInjector 的时候传入的。
找到 MainModule_ProvideMainPresenterFactory 的 get 方法。如下所示:

@Override
public MainPresenter get() {  
  MainPresenter provided = module.provideMainPresenter();
  if (provided == null) {
    throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
  }
  return provided;
}

其内部是调用了 MainModule 对象的工厂方法 provideMainPresenter 方法来生成 MainPresenter 对象。如下:

@Module
public class MainModule {

    @Provides MainPresenter provideMainPresenter(){
        return new MainPresenter();
    }
}

分析完毕!
总结
DaggerMainComponent 在创建的时候,需要传入 Module,这个传入的 Module 到底用来干嘛的? Factory 的 get 方法中会调用 Mudule 的工厂方法获取到依赖。

DaggerMainComponent 在创建的时候,还创建了 Factory(工厂类,对应上文的 MainModule_ProvideMainPresenterFactory) 和MembersInjector(成员注入类,对应上文的 MainActivity_MembersInjector) 。

当 DaggerMainComponent 调用 inject 方法注入依赖的时候,会调用 MembersInjector 的 injectMembers 方法,在 injectMembers 方法中会调用Factory 的 get 方法获取到依赖(get 方法中实际会调用 Mudule 的工厂方法获取到依赖),最后将依赖赋值给 目标类 的成员变量。完成依赖注入。

最后,你会发现这篇文章没有使用到 @Qulifier, @Scope(我觉的@Scope注解不好理解)。而且Module类也只有一个方法,如果有多个方法呢?生成的辅助类又会如何?
学习资料
https://google.github.io/dagger/
神兵利器Dagger2
Android:dagger2让你爱不释手-基础依赖注入框架篇

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

推荐阅读更多精彩内容