Dagger2详解-从代码分析其原理

Dagger2基本概念请移步官方文档,或者我的翻译

架构方面请关注GitHub(MVP+Retrofit+Dagger2+Okhttp)及我的文章Android UI框架快速搭建实践

Dagger2是Dagger1的分支,由谷歌公司接手开发,目前的版本是2.0。Dagger2解决问题的基本思想是:利用生成和写的代码混合达到看似所有的产生和提供依赖的代码都是手写的样子。

如果我们将Dagger2和1比较,他们两个在很多方面都非常相似,但也有很重要的区别,如下:

  1. 再也没有使用反射:图的验证、配置和预先设置都在编译的时候执行。
  1. 容易调试和可跟踪:完全具体地调用提供和创建的堆栈
  2. 更好的性能:谷歌声称他们提高了13%的处理性能
  3. 代码混淆:使用派遣方法,就如同自己写的代码一样

当然所有这些很棒的特点都需要付出一个代价,那就是缺乏灵活性,例如:Dagger2没用反射所以没有动态机制。

所以在Android UI框架快速搭建实践这篇文章中,因为我将Presenter作为泛型成员变量抽取到BaseFragment中了,所以View层的Presenter无法使用Dagger2实现注入。如果要实现Presenter注入,则需要在每个View实现中注入对应的Presenter实例,这样就无法抽取到基类中了。这个在具体实践中,需要你自己权衡。

本文就GitHub项目,从生成的代码对Dagger2进行分析。

使用Dagger2,在module的gradle中配置:

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'

android{
    ...
}
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        //添加apt插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    ...
 }

使用时,定义好component及module后,build工程,Dagger2编译器就会生成对应代码,然后在需要注入的地方调用生成的component实现,名为Dagger前缀+component名称的类完成绑定。

先看结构图:

dagger2.png

大概介绍一下基本概念:

  • Module:使用@Module注解,封装@Provides方法,@Provides注解方法提供依赖对象。

  • @Inject:在需要依赖注入的地方使用。Dagger2提供3种方式:

    • 构造方法注入:在类的构造方法前面注释@Inject
    • 成员变量注入:在类的成员变量(非私有)前面注释@Inject
    • 函数方法注入:在函数前面注释@Inject
  • @Peractivity:自定义scope注解,约束依赖对象的生命周期。

  • @Singleton:api提供的scope注解,保证对象在对象图中唯一。

  • Component:使用@Component注解的接口,是@Inject和@Module联系的桥梁,子component可以使用@Subcomponent也可以指定@Component的dependency参数。

是时候上代码了(Dagger2生成的代码在build/generated/source/apt/目录下):

DemoApplicationModule的定义:

@Module
public class DemoApplicationModule {
  private final Application application;

  public DemoApplicationModule(Application application) {
    this.application = application;
  }

  /**
   * Expose the application to the graph.
   */
  @Provides @Singleton Application application() {
    return application;
  }

  @Provides @Singleton LocationManager provideLocationManager() {
    return (LocationManager) application.getSystemService(LOCATION_SERVICE);
  }
}

来看生成的代码:

public final class DemoApplicationModule_ApplicationFactory implements Factory<Application> {
  private final DemoApplicationModule module;

  public DemoApplicationModule_ApplicationFactory(DemoApplicationModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public Application get() {
    return Preconditions.checkNotNull(
        module.application(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<Application> create(DemoApplicationModule module) {
    return new DemoApplicationModule_ApplicationFactory(module);
  }
}   
----------------
public final class DemoApplicationModule_ProvideLocationManagerFactory
    implements Factory<LocationManager> {
  private final DemoApplicationModule module;

  public DemoApplicationModule_ProvideLocationManagerFactory(DemoApplicationModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public LocationManager get() {
    return Preconditions.checkNotNull(
        module.provideLocationManager(),
        "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<LocationManager> create(DemoApplicationModule module) {
    return new DemoApplicationModule_ProvideLocationManagerFactory(module);
  }
}

module中定义的@Provides方法会生成对应的工厂类,实现Factory<T>接口,复写get()方法,get()中实际调用了module的provide方法,那module实例从哪里来的呢,看工厂类的创建方法中,看来这个module对象是需要外部传入的。

ApplicationComponent定义:

@Singleton  
@Component(modules = DemoApplicationModule.class)
public interface ApplicationComponent {
  // Field injections of any dependencies of the DemoApplication
  void inject(DemoApplication application);

  // Exported for child-components.
  Application application();
  LocationManager locationManager();
}

生成的代码:

public final class DaggerApplicationComponent implements ApplicationComponent {
  private Provider<LocationManager> provideLocationManagerProvider;

  private MembersInjector<DemoApplication> demoApplicationMembersInjector;

  private Provider<Application> applicationProvider;

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

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideLocationManagerProvider =
        DoubleCheck.provider(
            DemoApplicationModule_ProvideLocationManagerFactory.create(
                builder.demoApplicationModule));

    this.demoApplicationMembersInjector =
        DemoApplication_MembersInjector.create(provideLocationManagerProvider);

    this.applicationProvider =
        DoubleCheck.provider(
            DemoApplicationModule_ApplicationFactory.create(builder.demoApplicationModule));
  }

  @Override
  public void inject(DemoApplication application) {
    demoApplicationMembersInjector.injectMembers(application);
  }

  @Override
  public Application application() {
    return applicationProvider.get();
  }

  @Override
  public LocationManager locationManager() {
    return provideLocationManagerProvider.get();
  }

  public static final class Builder {
    private DemoApplicationModule demoApplicationModule;

    private Builder() {}

    public ApplicationComponent build() {
      if (demoApplicationModule == null) {
        throw new IllegalStateException(
            DemoApplicationModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerApplicationComponent(this);
    }

    public Builder demoApplicationModule(DemoApplicationModule demoApplicationModule) {
      this.demoApplicationModule = Preconditions.checkNotNull(demoApplicationModule);
      return this;
    }
  }
}   

DaggerApplicationComponent内部生成了Builder类,通过Builder的build()可以得到DaggerApplicationComponent对象,但必须先传入DemoApplicationModule对象,这是当然的,Component本来就只是一个桥梁而已,别忘了之前定义ApplicationComponent的注解@Component(modules = DemoApplicationModule.class),这样就好理解了吧。

调用了Builder的build()后,会走DaggerApplicationComponent的构造器方法,这里调用了initialize(builder), initialize(builder)又干了什么呢?这里初始化了待注入的依赖对象locationManager和application,通过之前的工厂类的create()方法得到工厂类对象,工厂类是实现Factory<T>接口的,Factory<T>又是继承Provider<T>的,所以这里相当于拿到的是封装了不同对象的Provider实例, DoubleCheck.provider(provider)又干了什么呢?DoubleCheck也实现了Provider<T>,它的provider(provider)方法实际上返回了本身实例,实际上也是一个Provider<T>,但为什么要这么做呢?看代码:

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;
}

public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = provider.get();
        provider = null;
        }
      }
    }
    return (T) result;
  }

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

再看注入的地方:

public class DemoApplication extends Application {
  private ApplicationComponent applicationComponent;

  @Inject LocationManager locationManager;  
  @Override public void onCreate() {
    super.onCreate();
    applicationComponent = DaggerApplicationComponent.builder()
        .demoApplicationModule(new DemoApplicationModule(this))
        .build();
  }

  public ApplicationComponent component() {
    return applicationComponent;
  }
}

这里需要注入locationMnager,我们再看根据@Inject生成的代码:

public final class DemoApplication_MembersInjector implements MembersInjector<DemoApplication> {
  private final Provider<LocationManager> locationManagerProvider;

  public DemoApplication_MembersInjector(Provider<LocationManager> locationManagerProvider) {
    assert locationManagerProvider != null;
    this.locationManagerProvider = locationManagerProvider;
  }

  public static MembersInjector<DemoApplication> create(
      Provider<LocationManager> locationManagerProvider) {
    return new DemoApplication_MembersInjector(locationManagerProvider);
  }

  @Override
  public void injectMembers(DemoApplication instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.locationManager = locationManagerProvider.get();
  }

  public static void injectLocationManager(
      DemoApplication instance, Provider<LocationManager> locationManagerProvider) {
    instance.locationManager = locationManagerProvider.get();
  }
}

injectMembers(DemoApplication instance) 方法将locationManager对象赋值给DemoApplication对象,DemoApplication和locationManagerProvider从哪里来呢?上面DaggerApplicationComponent的initialize(builder)中实例化了DemoApplication_MembersInjector并传入需要的参数。

DaggerApplicationComponent实现了ApplicationComponent,当然要复写其方法。
在DemoApplication中调用DaggerApplicationComponent的inject()方法,然后就会调用DemoApplication_MembersInjector的injectMembers(DemoApplication instance),就实现了依赖注入。

那DaggerApplicationComponent的其他方法呢?比如locationManager(),application()等呢,这些方法是在定义在ApplicationComponent中的,子component是共享父component中的绑定的,那子component怎么拿到父component中的依赖对象呢?

 this.locationManagerProvider =
        new Factory<LocationManager>() {
          private final ApplicationComponent applicationComponent = builder.applicationComponent;

          @Override
          public LocationManager get() {
            return Preconditions.checkNotNull(
                applicationComponent.locationManager(),
                "Cannot return null from a non-@Nullable component method");
          }
        };

在DaggerHomeComponent的initialize(final Builder builder)中通过applicationComponent.locationManager,子component就也有locationManager实例了。

Activity的依赖注入分析也是类似的,通过源码,我们可以清晰的跟踪其调用流程。

That's all!希望能帮到你。

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

推荐阅读更多精彩内容