DI理解以及Dagger生成代码分析

前言

依赖注入是我在安卓项目架构学习(dagger+mvp+retrofit+rxjava+eventbus)里面的又一大步。通过一天多的系统学习(看别人的项目源码,博客文章,自己看dagger的生成源码),差不多也可以去自己总结一下了。dagger2的用法网上太多教程了,我就不再描述了。这里我只总结依赖注入的概念,在项目架构中的作用以及部分最基础的生成源码个人分析。建议大家在学习dagger之前先去学习java注解和反射,因为dagger确实不好理解,如果有了注解基础学习起来会更轻松。

以前看人家大牛的项目都有个inject包,以前想当然的以为就是个注解包。。也不懂里面的component和module包是干嘛的。现在想想,也又算一种进步吧!

大家可以通过我这个练手app巩固dagger的用法 :githubQuery

依赖注入?(DI and IOC)

其实依赖注入也是为了模块解耦,你会发现一切一切,不论是mvp还是dagger都是为了模块解耦。dagger和mvp结合起来还可以做到把m-v-p之间进一步解耦。所谓耦合就是两个模块联系在了一起,什么意思呢?比如一个模块A里面new了另一个模块B的对象,这时候就说两个模块耦合在了一起,如果现在B模块的构造函数做了修改,那么你还需要去修改模块A里面的代码。而我们希望的是B无论怎么修改都不会影响模块A。(联系mvp模式,无论m层怎么修改,都不会影响v层)而依赖注入可以用两种方法来解决:

1.传递依赖:

class A{
  //...
  B b;
  public A(B b){
  this.b = b;
  }
}

或者传递给 B的更高层级的抽象层(面向接口编程的思想)。

2.注入依赖。(如dagger di框架)

其实这种解决办法就叫做控制反转(ioc)。把添加依赖的权利给外部,自己不去主动添加依赖关系

好像有人分不大清楚dagger和butterknife有啥区别。。其实区别很大,butterkinfe也是通过注解来实现依赖注入,不过他做的是view的依赖注入。而dagger做的是更高层次的模块之间的依赖注入。

话说以前还写了一个很简单的view依赖注入框架(用的反射),我记得我以前在github上面写的是依赖注入框架,后来赶紧改成了view依赖注入框架。。

依赖注入器?

如果说用传递依赖的话,那么如果模块A需要模块B的大量对象,或者说依赖程度很高,那么传递函数里面的依赖参数将会很多。

而使用注入器可以极大的减少代码量,模块之间的依赖关系也会很清晰。把注入器看作项目的一个模块(inject)专门负责把某些模块注入到他的依赖中去。当然说这些你可能还是不能体会到它的好处,多去写写,看看人家的项目代码才能慢慢体会。(比如我把所有的基础配置以及application的context全部提供在applicaion的module,然后直接把这个module注入到baseactivity或者application中去,然后其他的activity module再去继承这个application module。一行代码就去注入很多的依赖!)

Dagger2

dagger2就是现在一个比较火的依赖注入器框架。它使用的预编译期间生成代码完成依赖,而不是用的反射。。都知道反射对手机应用开发影响是比较大的。我学习这个框架就把它想成了一个注射器,component是针管,module是注射瓶,里面的依赖对象是注入的药水,build方法是插进患者,inject方法的调用是推动活塞。这样形象的理解还是很容易理解的!!

dagger需要掌握的就是component, module, scope, provides, singleton.

下面来探讨部分生成源码吧。


描述:我现在有两个medule,并且其中一个component依赖另一个component。代码如下

/**
 * Created by Zane on 16/1/21.
 */
@Module
public class ActivityModule {
    @Provides
    UserModel provideUserModel(){
        return new UserModel();
    }
}

@Module
public class ActivityModule2 {
    @Provides
    UserModelTwo provideUserModelTwo(){
        return new UserModelTwo();
    }
}

@Component(modules = ActivityModule.class)
public interface ActivityComponent {
    //void inject(Activity activity);
    UserModel userModel();
}

@Component(dependencies = ActivityComponent.class, modules = ActivityModule2.class)
public interface ActivityComponent2 {
    void inject(MainActivity activity);
}

两个model/bean代码如下:

/**
 * Created by Zane on 16/1/21.
 */
public class UserModel {

    private String name = "xuzhi";
    private String age = "20";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

public class UserModelTwo {

    private String phone = "1888";

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

在activity中的注入代码如下:

activityComponent = DaggerActivityComponent
                                    .builder()
                                    .activityModule(new ActivityModule())
                                    .build();
activityComponent2 = DaggerActivityComponent2
                                     .builder()
                                     .activityComponent(activityComponent)
                                     .activityModule2(new ActivityModule2())
                                     .build();
activityComponent2.inject(this);

我们都知道,编译之后dagger框架会自动给我们生成以dagger+xxxcomponent为名的类。如我的代码就是生成了DaggerActivityComponent类,并且通过一系列的方法调用最终构建成功。看过生成代码的人会感觉他用的就是包装器模式!我们首先来看DaggerActivityComponent的builder()方法:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerActivityComponent implements ActivityComponent {
  private Provider<UserModel> provideUserModelProvider;

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

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

可以看到生成了一个Provider<UserModel> provideUserModelProvider;,而在这个组件所对应的模块里面我们提供了user model的对象。DaggerActivityComponent的构造器先不管。一步步来,我们看到builder()方法return了一个Builder类型的对象,往下看你就会看到Builder这个静态内部类!代码如下:

public static final class Builder {
    private ActivityModule activityModule;

    private Builder() {
    }

    public ActivityComponent build() {
      if (activityModule == null) {
        this.activityModule = new ActivityModule();
      }
      return new DaggerActivityComponent(this);
    }

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

注入代码中,我们紧接着调用了activityModule(ActivityModule activityModule)方法。我们看到因为在ActivityComponent中我们用@component添加了ActivityModule。所以这里生成了这样一个方法来让你传递一个对象进来。之后我们调用了build()方法,这个方法里面调用了DaggerActivityComponent类的构造方法!

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

这里也没啥看的,继续看initialize(builder);方法:

private void initialize(final Builder builder) {
    this.provideUserModelProvider =     ActivityModule_ProvideUserModelFactory.create(builder.activityModule);
  }

这里有个新类,叫做xxxxFactory,这个类很重要,点进去继续看creat方法:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ActivityModule_ProvideUserModelFactory implements Factory<UserModel> {
  private final ActivityModule module;

  public ActivityModule_ProvideUserModelFactory(ActivityModule module) {
    assert module != null;
    this.module = module;
  }

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

  public static Factory<UserModel> create(ActivityModule module) {
    return new ActivityModule_ProvideUserModelFactory(module);
  }
}

很简单,就是把我们的ActivityModule存在了这个类里面,并提供一个get方法,这个get方法之后会用到。

DaggerActivityComponent类的代码我们就看完了,很简单,因为这个针头并没有提供inject方法哈哈,而是多重依赖给了ActivityComponent2.接下来看DaggerActivityComponent2的代码:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerActivityComponent2 implements ActivityComponent2 {
  private Provider<UserModel> userModelProvider;
  private Provider<UserModelTwo> provideUserModelTwoProvider;
  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

第一个module中的usermodel也被注入到了第二个module中!接着看Builder类

public static final class Builder {
    private ActivityModule2 activityModule2;
    private ActivityComponent activityComponent;

    private Builder() {
    }

    public ActivityComponent2 build() {
      if (activityModule2 == null) {
        this.activityModule2 = new ActivityModule2();
      }
      if (activityComponent == null) {
        throw new IllegalStateException("activityComponent must be set");
      }
      return new DaggerActivityComponent2(this);
    }

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

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

ActivityModule2 activityModule2,ActivityComponent activityComponent;都是这个类的静态变量,因为一个是依赖一个是对应的 module。然后你可以自己看看注入代码,我们确实是分别调用了activityComponent(ActivityComponent activityComponent),activityModule2(ActivityModule2 activityModule2)之后才去调用build();

private void initialize(final Builder builder) {
    this.userModelProvider = new Factory<UserModel>() {
      private final ActivityComponent activityComponent = builder.activityComponent;
      @Override public UserModel get() {
        UserModel provided = activityComponent.userModel();
        if (provided == null) {
          throw new NullPointerException("Cannot return null from a non-@Nullable component method");
        }
        return provided;
      }
    };
    this.provideUserModelTwoProvider = ActivityModule2_ProvideUserModelTwoFactory.create(builder.activityModule2);
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), userModelProvider, provideUserModelTwoProvider);
  }

最上面有一段代码,其用意相当于重新构建一个xxxxFactory类。你可以看到之前那个xxxFactory类是实现了Factory<xxx>接口,而这里则是用匿名内部类做到了,为什么要这么做呢,我认为是因为这个变量是继承过来的,所以不应该再去重复creat而是通过activitycomponent来调用userModel()方法来获得。然后我们就把两个成员变量都初始化了!之后再来看注入,我们点到MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), userModelProvider, provideUserModelTwoProvider);方法去看:

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

  public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<UserModel> modelProvider, Provider<UserModelTwo> model2Provider) {
    assert supertypeInjector != null;
    this.supertypeInjector = supertypeInjector;
    assert modelProvider != null;
    this.modelProvider = modelProvider;
    assert model2Provider != null;
    this.model2Provider = model2Provider;
  }

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

  public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<UserModel> modelProvider, Provider<UserModelTwo> model2Provider) {
      return new MainActivity_MembersInjector(supertypeInjector, modelProvider, model2Provider);
  }
}

很清楚了,也是一种包装器模式。我们可以类比,每一个被注入的类都会生成一个xxx_MembersInject类。看到我们把两个Factory对象都传进来了,并且调用了get方法来获得我们需要注入的依赖对象。大功告成,最后看DaggerActivityComponent2里面的最后一个方法:

@Override
  public void inject(MainActivity activity) {
    mainActivityMembersInjector.injectMembers(activity);
  }

对!就是我们MainActivity里面调用的inject()方法。对应上一份源码看,懂了吧!这就是注入过程,所以我把inject()方法的调用比做活塞的推动。


所以看了这么多,其实就是想多了解一下dagger如何运作,我们的调用代码tmd为什么要这么写。嗯,就是这样!之前学这个也是越看越烦啦,后来慢慢的总结一下也是极好的。下面就准备开始用mvp+dagger2+rxjava+retrofit来搞点事情了。并且我还不准备用我自己的mvp框架,感觉写的太渣了,分分钟爆炸的节奏。。

未经博主同意,不得转载该篇文章

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

推荐阅读更多精彩内容