一.Dagger2注入原理

Dagger2以自动生成代码的形式,帮助我们构建依赖图,在使用依赖的时候方便清晰,这里说明一点,在我们使用Dagger2的时候,绝大多数错误都是编译器就会暴漏出来,这也就决定了这套框架的稳定性会更高。

关于生成的源码,我们一起看一下。我们就以之前提到的例子来看:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerApplicationComponent implements ApplicationComponent {
private Provider<Application> applicationProvider;
private Provider<Context> contextProvider; private DaggerApplicationComponent(Builder builder) {
assert builder != null;
initialize(builder);
} public static Builder builder() {
return new Builder();
} private void initialize(final Builder builder) {
this.applicationProvider = ApplicationModule_ApplicationFactory.create(builder.applicationModule);
this.contextProvider = ApplicationModule_ContextFactory.create(builder.applicationModule);
} @Override
public Application application() {
return applicationProvider.get();
} @Override
public Context context() {
return contextProvider.get();
} public static final class Builder {
private ApplicationModule applicationModule;
private GsonModule gsonModule; private Builder() {
} public ApplicationComponent build() {
if (applicationModule == null) {
throw new IllegalStateException("applicationModule must be set");
}
if (gsonModule == null) {
this.gsonModule = new GsonModule();
}
return new DaggerApplicationComponent(this);
} public Builder applicationModule(ApplicationModule applicationModule) {
if (applicationModule == null) {
throw new NullPointerException("applicationModule");
}
this.applicationModule = applicationModule;
return this;
} public Builder gsonModule(GsonModule gsonModule) {
if (gsonModule == null) {
throw new NullPointerException("gsonModule");
}
this.gsonModule = gsonModule;
return this;
}
}
}

可以看到DaggerApplicationComponent中,有一个建造者模式来构建一个ApplicationComponent对象,这也帮我们初始化了两个Module中的依赖,但是注意,这里并没有直接初始化所有模块内的依赖,而只是初始化了组件对象而已。

可以看到initialize方法中有两个工厂方法。

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ApplicationModule_ContextFactory implements Factory<Context> {
private final ApplicationModule module; public ApplicationModule_ContextFactory(ApplicationModule module) {
assert module != null;
this.module = module;
} @Override
public Context get() {
Context provided = module.context();
if (provided == null) {
throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
}
return provided;
} public static Factory<Context> create(ApplicationModule module) {
return new ApplicationModule_ContextFactory(module);
}
}

可以看出这个工厂中一直保留着ApplicationModule,当我们每次获取依赖,则会重新调用Module的context()方法,即如果里面是new的形式提供依赖 ,则会重新创建对象。

而反观我们用PerActivity标注的ToasterProvider:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerActivityComponent implements ActivityComponent {
private Provider<Context> contextProvider;
private MembersInjector<BaseActivity> baseActivityMembersInjector;
private Provider<Toaster> provideToasterProvider;
private Provider<Toaster> provideTheToasterProvider; private DaggerActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
} public static Builder builder() {
return new Builder();
} private void initialize(final Builder builder) {
this.contextProvider = new Factory<Context>() {
private final ApplicationComponent applicationComponent = builder.applicationComponent;
@Override public Context get() {
Context provided = applicationComponent.context();
if (provided == null) {
throw new NullPointerException("Cannot return null from a non-@Nullable component method");
}
return provided;
}
};
this.baseActivityMembersInjector = BaseActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), contextProvider);
this.provideToasterProvider = ScopedProvider.create(ActivityUtilModule_ProvideToasterFactory.create(builder.activityUtilModule));
this.provideTheToasterProvider = ActivityUtilModule_ProvideTheToasterFactory.create(builder.activityUtilModule);
} @Override
public void inject(BaseActivity activity) {
baseActivityMembersInjector.injectMembers(activity);
} @Override
public Toaster theToaster() {
return provideToasterProvider.get();
} @Override
public Toaster toaster() {
return provideTheToasterProvider.get();
} public static final class Builder {
private ActivityUtilModule activityUtilModule;
private ApplicationComponent applicationComponent; private Builder() {
} public ActivityComponent build() {
if (activityUtilModule == null) {
throw new IllegalStateException("activityUtilModule must be set");
}
if (applicationComponent == null) {
throw new IllegalStateException("applicationComponent must be set");
}
return new DaggerActivityComponent(this);
} public Builder activityUtilModule(ActivityUtilModule activityUtilModule) {
if (activityUtilModule == null) {
throw new NullPointerException("activityUtilModule");
}
this.activityUtilModule = activityUtilModule;
return this;
} public Builder applicationComponent(ApplicationComponent applicationComponent) {
if (applicationComponent == null) {
throw new NullPointerException("applicationComponent");
}
this.applicationComponent = applicationComponent;
return this;
}
}
}

provideToasterProvider则和其他初始化方式不同,可以看到后面ProviderToaster的工厂很普通:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ActivityUtilModule_ProvideToasterFactory implements Factory<Toaster> {
private final ActivityUtilModule module; public ActivityUtilModule_ProvideToasterFactory(ActivityUtilModule module) {
assert module != null;
this.module = module;
} @Override
public Toaster get() {
Toaster provided = module.provideToaster();
if (provided == null) {
throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
}
return provided;
} public static Factory<Toaster> create(ActivityUtilModule module) {
return new ActivityUtilModule_ProvideToasterFactory(module);
}
}

那么ScopedProvider.create包住的工厂有什么特别的呢?

public final class ScopedProvider<T> implements Provider<T> {
private static final Object UNINITIALIZED = new Object(); private final Factory<T> factory;
private volatile Object instance = UNINITIALIZED; private ScopedProvider(Factory<T> factory) {
assert factory != null;
this.factory = factory;
} @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
@Override
public T get() {
// double-check idiom from EJ2: Item 71
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = factory.get();
}
}
}
return (T) result;
} /** Returns a new scoped provider for the given factory. */
public static <T> Provider<T> create(Factory<T> factory) {
if (factory == null) {
throw new NullPointerException();
}
return new ScopedProvider<T>(factory);
}
}

我们看下源码,这里的create是创建了一个ScopeProvider的对象,并将工厂传入,当这个ScopeProvider去get的时候,内部有个单例来维持这个对象,这就是为什么我们自定义注解是个单例的秘密。

接下来看看神器的注解是怎么起作用的。

public final class BaseActivity_MembersInjector implements MembersInjector<BaseActivity> {
private final MembersInjector<FragmentActivity> supertypeInjector;
private final Provider<Context> contextProvider; public BaseActivity_MembersInjector(MembersInjector<FragmentActivity> supertypeInjector, Provider<Context> contextProvider) {
assert supertypeInjector != null; this.supertypeInjector = supertypeInjector; assert contextProvider != null; this.contextProvider = contextProvider;
} public void injectMembers(BaseActivity instance) {
if(instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
} else {
this.supertypeInjector.injectMembers(instance);
instance.context = (Context)this.contextProvider.get();
}
} public static MembersInjector<BaseActivity> create(MembersInjector<FragmentActivity> supertypeInjector, Provider<Context> contextProvider) {
return new BaseActivity_MembersInjector(supertypeInjector, contextProvider);
}
}

在我们注入这个对象后,injectMembers方法中,写明了BaseActivity中的context对象是从ContextProvider的get方法中得到,根据代码也可以看到,这部分依赖是从ApplicationComponent中的provideContext方法取得。

再看看Lazy Load:

public final class DoubleCheckLazy<T> implements Lazy<T> {
private static final Object UNINITIALIZED = new Object(); private final Provider<T> provider;
private volatile Object instance = UNINITIALIZED; private DoubleCheckLazy(Provider<T> provider) {
assert provider != null;
this.provider = provider;
} @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
@Override
public T get() {
// to suppress it.
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = provider.get();
}
}
}
return (T) result;
} public static <T> Lazy<T> create(Provider<T> provider) {
if (provider == null) {
throw new NullPointerException();
}
return new DoubleCheckLazy<T>(provider);
}
}

toasterLazy加载的时候是使用DoubleCheckLazy.create(this.toasterLazyProvider)来进行初始化。而内部是个单例,只有在get时才会初始化。

至此,Dagger2的主要源码就差不多理解了。

二.使用分析

Dagger2很优雅,优雅到你可以特别轻松的调试它,以为他和你手写的代码几乎一模一样,又优雅到几乎所有的错误都从编译器暴露出。

与RoboGuice的反射不同,生成代码必然会导致方法数的增加。但是,我们可以看到生成的代码数量并不多,而且在实际应用过程中也可以看出,确实影响不大,这个可以大家在以后的使用中慢慢体会。

性能上Dagger2会优于RoboGuice,尤其是天生支持懒加载,但是在易用性上,RoboGuice更容易上手和理解,并且针对Android做了很多通用依赖,为项目开发提高便利。

综上,如果是一个小而美的应用,使用RoboGuice可以快速帮你完成开发工作,而Dagger2在长期来看,性能和效率更佳。

至此,Dagger2入坑系列结束。

Dagger2 (三) 总结篇的更多相关文章

  1. Delphi 泛型(三十篇)

    Delphi 泛型(三十篇)http://www.cnblogs.com/jxgxy/category/216671.html

  2. Dagger2 (二) 进阶篇

    一.作用域Scope 之前了解RoboGuice的时候,我们知道它默认给我们提供了几个注解,ContextSingleton和Singleton,但是Dagger2更为灵活,只有javax包中提供的S ...

  3. JavaScript 面向对象(三) —— 高级篇

    JavaScript 面向对象(一) —— 基础篇 JavaScript 面向对象(二) —— 案例篇 一.json方式的面向对象 首先要知道,js中出现的东西都能够放到json中.关于json数据格 ...

  4. 读懂IL代码就这么简单(三)完结篇

    一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍 这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认 ...

  5. ios -- 教你如何轻松学习Swift语法(三) 完结篇

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.自动引用计数   1.自动引用计数工作机制      1.1 swift和o ...

  6. Android UI开发第三十三篇——Navigation Drawer For Android API 7

    Creating a Navigation Drawer中使用的Navigation Drawer的android:minSdkVersion="14",现在Android API ...

  7. Android UI开发第三十一篇——Android的Holo Theme

    好长时间没写Android UI方面的文章了,今天就闲扯一下Android的Holo主题.一直做android开发的可能都知道,Android 系统的UI有过两次大的变化,一次是android 3.0 ...

  8. JDK源码学习--String篇(三) 存储篇

    在进一步解读String类时,先了解下内存分配和数据存储的. 数据存储 1.寄存器:最快的存储区,位于处理器的内部.由于寄存器的数量有限,所以寄存器是按需分配. 2.堆栈:位于RAM中,但是通过堆栈指 ...

  9. Java 面试知识点解析(三)——JVM篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

随机推荐

  1. 微软“.Net社区虚拟大会”dotnetConf2015:关键词:.NET 创新、开源、跨平台

    去年 11 月的时候,微软开源了 .NET CoreFX,然后是今年 2 月份的 .NET CoreCLR.自那时以来,已经有大约 3500 人在 GitHub 上进行了提交,而且贡献者的人数还在持续 ...

  2. .NET基础拾遗(3)字符串、集合和流

    Index: (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基础 ...

  3. 站在风口,你或许就是那年薪20w+的程序猿

    最近面试了一些人,也在群上跟一些群友聊起,发现现在的互联网真是热,一些工作才两三年的期望的薪资都是十几K的起,这真是让我们这些早几年就成为程序猿的情何以堪!正所谓是站在风口上,猪也能飞起来!我在这里就 ...

  4. springMvc的搭建

    学习SpringMVC--从HelloWorld开始   前言: 时隔十二年,中国女排最终过关斩将,用3:1的成绩证明了自己的实力,霸气夺冠,为中国赢得了一枚意义非常的金牌.这是一次全民的狂欢,一场视 ...

  5. EMC与电容(二)-电容参数意义、各电容的特点及应用

    上次的问题,看到很多回答里都有关于X电容,Y电容,NPO之类,这些很奇怪的参数到底代表什么意义呢?以前很多次都在BOM表里看到这些参数,一直都无视过去,正好这次的EMC课程里也提到这方面的知识,正好跟 ...

  6. SQL Server 游标运用:鼠标轨迹字符串分割

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 游标模板(Cursor Template) 鼠标轨迹字符串分割SQL脚本实现(SQL Code ...

  7. Jquery Uploadify3.21.与2.1版本 使用中存在的问题--记录三

    Jquery Uploadify是个上传插件. 2.1版本与3.2.1版本有很大区别,方法名跟参数变动较大 1.uploader:该属性是用来存放swf的路径,这个swf就是一个Flash的一个图标, ...

  8. Ubuntu 14.04 中 安装elasticsearch2.*+logstash2.*+kibana

    在Ubuntu 14.04 上安装单机版ELK 2.*(脚本化) 1.判断是否为root权限 if [ "${UID}" -ne 0 ]; then echo "You ...

  9. 浅析MySQL复制

    MySQL的复制是基于binlog来实现的. 流程如下 涉及到三个线程,主库的DUMP线程,从库的IO线程和SQL线程. 1. 主库将所有操作都记录到binlog中.当复制开启时,主库的DUMP线程根 ...

  10. nodejs、npm、grunt——名词解释

    最近着手开发一个新项目,打算从工程化的角度整理一套自己的前端开发.发布体系. grunt这些工具,之前别人用我也用,并没有认真想过它们的前世今生,正好趁着这个机会,我来理一理目前业界比较流行这些工具的 ...