Dagger2入门学习记录

最近在重新搭建android工程听到dagger2这个框架,然后去Github上看了一下,天啊!2w多的star,呃,是应该看一下这玩意是个什么东西了,竟然这么火。

百度了一下大概看了一下其他关于dagger的文章大概知道这玩意是干啥的了。

Dagger2是Dagger的升级版,是一个依赖注入框架,现在由Google接手维护。

我们要搞清楚这个框架怎么用必须先知道什么是依赖注入和依赖注入的好处。

什么是依赖注入

依赖注入是面向对象编程的一种设计模式,其目的是为了降低程序耦合,这个耦合就是类之间的依赖引起的。

举个例子:我们在写面向对象程序时,往往会用到组合,即在一个类中引用另一个类,从而可以调用引用的类的方法完成某些功能,就像下面这样.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ClassA {
...
ClassB b;
...
public ClassA() {
b = new ClassB();
}

public void do() {
...
b.doSomething();
...
}
}

这个时候就产生了依赖问题,ClassA依赖于ClassB,必须借助ClassB的方法,才能完成一些功能。这样看好像并没有什么问题,但是我们在ClassA的构造方法里面直接创建了ClassB的实例,问题就出现在这,在ClassA里直接创建ClassB实例,违背了单一职责原则,ClassB实例的创建不应由ClassA来完成;其次耦合度增加,扩展性差,如果我们想在实例化ClassB的时候传入参数,那么不得不改动ClassA的构造方法,不符合开闭原则。

因此我们需要一种注入方式,将依赖注入到宿主类(或者叫目标类)中,从而解决上面所述的问题。依赖注入有以下几种方式:

通过接口注入

1
2
3
4
5
6
7
8
9
10
11
12
interface ClassBInterface {
void setB(ClassB b);
}

public class ClassA implements ClassBInterface {
ClassB classB;

@override
void setB(ClassB b) {
classB = b;
}
}

通过set方法注入

1
2
3
4
5
6
7
public class ClassA {
ClassB classB;

public void setClassB(ClassB b) {
classB = b;
}
}

通过构造方法注入

1
2
3
4
5
6
7
public class ClassA {
ClassB classB;

public void ClassA(ClassB b) {
classB = b;
}
}

通过Java注解

1
2
3
4
5
6
7
public class ClassA {
//此时并不会完成注入,还需要依赖注入框架的支持,如RoboGuice,Dagger2
@inject ClassB classB;

...
public ClassA() {}
}

在Dagger2中用的就是最后一种注入方式,通过注解的方式,将依赖注入到宿主类中。

这些框架的Java注解是怎么实现的

从JDK5开始,Java增加了Annotation(注解),Annotation是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证、处理或者进行部署。

Annotation提供了一种为程序元素(包、类、构造器、方法、成员变量、参数、局域变量)设置元数据的方法。Annotation不能运行,它只有成员变量,没有方法。Annotation跟public、final等修饰符的地位一样,都是程序元素的一部分,Annotation不能作为一个程序元素使用。

定义Annotation

定义新的Annotation类型使用@interface关键字(在原有interface关键字前增加@符号)。定义一个新的Annotation类型与定义一个接口很像,例如:

1
2
3
public @interface Test{

}

定义完该Annotation后,就可以在程序中使用该Annotation。使用Annotation,非常类似于public、final这样的修饰符,通常,会把Annotation另放一行,并且放在所有修饰符之前。例如:

1
2
3
4
5
6
@Test
public class MyClass{

....

}

成员变量

Annotation只有成员变量,没有方法。Annotation的成员变量在Annotation定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。例如:

1
2
3
4
public @interface MyTag{
string name();
int age();
}

示例中定义了2个成员变量,这2个成员变量以方法的形式来定义。

一旦在Annotation里定义了成员变量后,使用该Annotation时就应该为该Annotation的成员变量指定值。例如:

1
2
3
4
5
6
public class Test{
@MyTag(name="红薯",age=30)
public void info(){
......
}
}

也可以在定义Annotation的成员变量时,为其指定默认值,指定成员变量默认值使用default关键字。示例:

1
2
3
4
public @interface MyTag{
string name() default "我兰";
int age() default 18;
}

如果Annotation的成员变量已经指定了默认值,使用该Annotation时可以不为这些成员变量指定值,而是直接使用默认值。例如:

1
2
3
4
5
6
public class Test{
@MyTag
public void info(){
......
}
}

根据Annotation是否包含成员变量,可以把Annotation分为如下两类:

  • 标记Annotation:没有成员变量的Annotation被称为标记。这种Annotation仅用自身的存在与否来为我们提供信息,例如@override等。

  • 元数据Annotation:包含成员变量的Annotation。因为它们可以接受更多的元数据,因此被称为元数据Annotation。

元注解

在定义Annotation时,也可以使用JDK提供的元注解来修饰Annotation定义。JDK提供了如下4个元注解(注解的注解,不是上述的”元数据Annotation“):

@Retention

@Target

@Documented

@Inherited

@Retention

@Retention用于指定Annotation可以保留多长时间。

@Retention包含一个名为“value”的成员变量,该value成员变量是RetentionPolicy枚举类型。使用@Retention时,必须为其value指定值。value成员变量的值只能是如下3个:

  • RetentionPolicy.SOURCE:Annotation只保留在源代码中,编译器编译时,直接丢弃这种Annotation。

  • RetentionPolicy.CLASS:编译器把Annotation记录在class文件中。当运行Java程序时,JVM中不再保留该Annotation。

  • RetentionPolicy.RUNTIME:编译器把Annotation记录在class文件中。当运行Java程序时,JVM会保留该Annotation,程序可以通过反射获取该Annotation的信息。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.demo1;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//name=value形式
//@Retention(value=RetentionPolicy.RUNTIME)

//直接指定
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTag{
String name() default "我兰";
}

如果Annotation里有一个名为“value“的成员变量,使用该Annotation时,可以直接使用XXX(val)形式为value成员变量赋值,无须使用name=val形式。

@Target

@Target指定Annotation用于修饰哪些程序元素。@Target也包含一个名为”value“的成员变量,该value成员变量类型为ElementType[ ],ElementType为枚举类型,值有如下几个:

  • ElementType.TYPE:能修饰类、接口或枚举类型

  • ElementType.FIELD:能修饰成员变量

  • ElementType.METHOD:能修饰方法

  • ElementType.PARAMETER:能修饰参数

  • ElementType.CONSTRUCTOR:能修饰构造器

  • ElementType.LOCAL_VARIABLE:能修饰局部变量

  • ElementType.ANNOTATION_TYPE:能修饰注解

  • ElementType.PACKAGE:能修饰包

示例1(单个ElementType):

1
2
3
4
5
6
7
8
9
package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
public @interface AnnTest {
String name() default "sunchp";
}

示例2(多个ElementType):

1
2
3
4
5
6
7
8
9
package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface AnnTest {
String name() default "sunchp";
}

@Documented

如果定义注解A时,使用了@Documented修饰定义,则在用javadoc命令生成API文档后,所有使用注解A修饰的程序元素,将会包含注解A的说明。

示例:

1
2
3
4
@Documented
public @interface Testable {

}
1
2
3
4
5
public class Test {
@Testable
public void info() {
}
}

@Inherited

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.demo2;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyTag{

......

}
1
2
3
4
5
6
package com.demo2;

@MyTag
public class Base {

}
1
2
3
4
5
6
7
8
9
package com.demo2;

//SubClass只是继承了Base类
//并未直接使用@MyTag注解修饰
public class SubClass extends Base {
public static void main(String[] args) {
System.out.println(SubClass.class.isAnnotationPresent(MyTag.class));
}
}

示例中Base使用@MyTag修饰,SubClass继承Base,而且没有直接使用@MyTag修饰,但是因为MyTag定义时,使用了@Inherited修饰,具有了继承性,所以运行结果为true。

如果MyTag注解没有被@Inherited修饰,则运行结果为:false。

基本Annotation

JDK默认提供了如下几个基本Annotation:

@Override

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package java.lang;

import java.lang.annotation.*;

/**
* Indicates that a method declaration is intended to override a
* method declaration in a supertype. If a method is annotated with
* this annotation type compilers are required to generate an error
* message unless at least one of the following conditions hold:
*
* <ul><li>
* The method does override or implement a method declared in a
* supertype.
* </li><li>
* The method has a signature that is override-equivalent to that of
* any public method declared in {@linkplain Object}.
* </li></ul>
*
* @author Peter von der Ah&eacute;
* @author Joshua Bloch
* @jls 9.6.1.4 Override
* @since 1.5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

限定重写父类方法。对于子类中被@Override 修饰的方法,如果存在对应的被重写的父类方法,则正确;如果不存在,则报错。@Override 只能作用于方法,不能作用于其他程序元素。

@Deprecated

用于表示某个程序元素(类、方法等)已过时。如果使用被@Deprecated修饰的类或方法等,编译器会发出警告。

@SuppressWarning

抑制编译器警告。指示被@SuppressWarning修饰的程序元素(以及该程序元素中的所有子元素,例如类以及该类中的方法…)取消显示指定的编译器警告。例如,常见的@SuppressWarning(value=”unchecked”)

@SafeVarargs

@SafeVarargs是JDK 7 专门为抑制“堆污染”警告提供的。

提取Annotation信息(反射)

当开发者使用了Annotation修饰了类、方法、Field等成员之后,这些Annotation不会自己生效,必须由开发者提供相应的代码来提取并处理Annotation信息。这些处理提取和处理Annotation的代码统称为APT(Annotation Processing Tool)。

JDK主要提供了两个类,来完成Annotation的提取:

  • java.lang.annotation.Annotation接口:这个接口是所有Annotation类型的父接口(后面会分析Annotation的本质,Annotation本质是接口,而java.lang.annotation.Annotation接口是这些接口的父接口)。

  • java.lang.reflect.AnnotatedElement接口:该接口代表程序中可以被注解的程序元素。

java.lang.annotation.Annotation

java.lang.annotation.Annotation接口源码:

1
2
3
4
5
6
7
8
9
10
11
12
package java.lang.annotation;

public interface Annotation {

boolean equals(Object obj);

int hashCode();

String toString();

Class<? extends Annotation> annotationType();
}

java.lang.annotation.Annotation接口的主要方法是annotationType( ),用于返回该注解的java.lang.Class。

java.lang.reflect.AnnotatedElement

java.lang.reflect.AnnotatedElement接口源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package java.lang.reflect;

import java.lang.annotation.Annotation;

public interface AnnotatedElement {

boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);

<T extends Annotation> T getAnnotation(Class<T> annotationClass);

Annotation[] getAnnotations();

Annotation[] getDeclaredAnnotations();
}

主要方法有:

  • isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否存在指定类型的注解,如果存在则返回true,否则返回false。

  • getAnnotation(ClassannotationClass):返回该程序元素上存在的指定类型的注解,如果该类型的注解不存在,则返回null

Annotation[] getAnnotations():返回该程序元素上存在的所有注解。

java.lang.reflect.AnnotatedElement接口是所有程序元素(例如java.lang.Class、java.lang.reflect.Method、java.lang.reflect.Constructor等)的父接口。

所以程序通过反射获取了某个类的AnnotatedElement对象(例如,A类method1()方法的java.lang.reflect.Method对象)后,就可以调用该对象的isAnnotationPresent( )、getAnnotation( )等方法来访问注解信息。

使用反射获取注解信息

给定一个类的全额限定名,加载类,并列出该类中被注解@MyTag修饰的方法和没被修饰的方法。

注解定义:

1
2
3
4
5
6
7
8
9
10
11
12
package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTag {

}

注解处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.demo1;

import java.lang.reflect.Method;

public class ProcessTool {
public static void process(String clazz) {
Class targetClass = null;

try {
targetClass = Class.forName(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

for (Method m : targetClass.getMethods()) {
if (m.isAnnotationPresent(MyTag.class)) {
System.out.println("被MyTag注解修饰的方法名:" + m.getName());
} else {
System.out.println("没被MyTag注解修饰的方法名:" + m.getName());
}
}
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
package com.demo1;

public class Demo {
public static void m1() {

}

@MyTag
public static void m2() {

}
}
1
2
3
4
5
6
7
8
package com.demo1;

public class Test {

public static void main(String[] args) {
ProcessTool.process("com.demo1.Demo");
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
没被MyTag注解修饰的方法名:m1
被MyTag注解修饰的方法名:m2
没被MyTag注解修饰的方法名:wait
没被MyTag注解修饰的方法名:wait
没被MyTag注解修饰的方法名:wait
没被MyTag注解修饰的方法名:equals
没被MyTag注解修饰的方法名:toString
没被MyTag注解修饰的方法名:hashCode
没被MyTag注解修饰的方法名:getClass
没被MyTag注解修饰的方法名:notify
没被MyTag注解修饰的方法名:notifyAll

注解本质

注解实质上会被编译器编译为接口,并且继承java.lang.annotation.Annotation接口。注解的成员变量会被编译器编译为同名的抽象方法。

额!我的天,看了这么老半天注释的原理,接下来我们进入正题来看看我们的Dagger框架。

引入Dagger

配置apt插件(在build.gradle(Project:xxx)中添加如下代码)

1
2
3
4
5
6
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
//添加apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

}

添加依赖(在build.gradle(Module:app)中添加如下代码)

1
2
3
4
5
6
7
8
9
10
11
12
apply plugin: 'com.android.application'
//添加如下代码,应用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
...
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java注解
compile 'org.glassfish:javax.annotation:10.0-b28'
...
}

使用Dagger2

先看一个例子,MainActivity依赖Province,Province依赖City,City依赖Street;

使用前

Street.java

1
2
3
4
5
6
public class Street {
public Street(){}
public String show(){
return "人民南路";
}
}

City.java

1
2
3
4
5
6
7
8
9
public class City {
public Street street;
public City(Street street) {
this.street = street;
}
public String show() {
return "成都市" + street.show();
}
}

Province.java

1
2
3
4
5
6
7
8
9
public class Province {
public City city;
public Province(City city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainActivity extends AppCompatActivity {
public Street street;
public City city;
public Province province;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
street = new Street();
city = new City(street);
province = new Province(city);
Log.d("hcy", "onCreate: " + province.showAddress());
}
}

可以看到,为了获取地址信息,在代码中需要实例化各种依赖到的对象,一旦依赖过多就容易影响代码阅读,那么配合使用@Inject和@Component又是怎样的呢?

使用后

Street.java

1
2
3
4
5
6
7
public class Street {
@Inject
public Street(){}
public String show(){
return "人民南路";
}
}

City.java

1
2
3
4
5
6
7
8
9
10
11
public class City {
@Inject
public Street street;
@Inject
public City(Street street) {
this.street = street;
}
public String show() {
return "成都市" + street.show();
}
}

Province.java

1
2
3
4
5
6
7
8
9
10
11
public class Province {
@Inject
public City city;
@Inject
public Province(City city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}

需要额外加一个接口,MainActivityComponent.java

1
2
3
4
@Component
public interface MainActivityComponent {
void inject(MainActivity activity);
}

此时的MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
public class MainActivity extends AppCompatActivity {
@Inject
public Province province;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//in Android Studio, select Build > Rebuild Project
DaggerMainActivityComponent.create().inject(this);
Log.d("hcy", "onCreate: " + province.showAddress());
}
}

前后的打印是一致的,可以看到MainActivity的中原本需要实例化对象的那些代码现在可以省略了,有助于我们更好地关注业务实现。回顾下使用注解的步骤:

  1. build.gradle中添加dagger2依赖

  2. 使用@Inject标注在构造函数和被引用的成员变量上

  3. 新建MainActivityComponent接口,并用@Component标注

  4. 在MainActivity中执行DaggerMainActivityComponent.create().inject(this);(第一次需Rebuild Project)

Demo源码下载:https://github.com/lxqxsyu/TestDagger

1
2
3
git clone git@github.com:lxqxsyu/TestDagger.git

git checkout demo1

源码分析

我们在MainaActivity中加了DaggerMainActivityComponent.create().inject(this)这句代码替换了之前的一些实例化的操作,那么这句代码具体做了哪些工作?原来Dagger2会在编译过程中生成对应的依赖项,这些依赖项在Android Studio该路径下,如图所示:

Dagger2生成的依赖路径

DaggerMainActivityComponent.create()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static MainActivityComponent create() {  
return builder().build();
}
public static Builder builder() {
return new Builder();
}

public static final class Builder {
private Builder() {
}

public MainActivityComponent build() {
return new DaggerMainActivityComponent(this);
}
}

可以看到,不管是通过builder().build()还是create(),最后都会调用DaggerMainActivityComponent构造函数;在通过源码阅读后,可以将整个过程分为两步,分别是initialize()和inject()

initialize()

1
2
3
4
5
6
7
8
9
10
11
12
private DaggerMainActivityComponent(Builder builder) {  
assert builder != null;
initialize(builder);
}

private void initialize(final Builder builder) {
this.cityMembersInjector = City_MembersInjector.create(Street_Factory.create());//注释1
this.cityProvider = City_Factory.create(cityMembersInjector, Street_Factory.create());//注释2
this.provinceMembersInjector = Province_MembersInjector.create(cityProvider);//注释3
this.provinceProvider = Province_Factory.create(provinceMembersInjector, cityProvider);//注释4
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provinceProvider);//注释5
}

在initialize()方法中对成员赋值,这里的成员分为两类,分别以_MembersInjector_Factory为后缀。

Xx_MembersInjector可以理解为注入器,用来实现Xx与内部成员的依赖关系:

1
2
3
public interface MembersInjector<T> {
void injectMembers(T instance);
}

Xx_Factory是创建Xx的工厂:

1
2
3
4
5
6
7
public interface Provider<T> {  
T get();
}

public interface Factory<T> extends Provider<T> {

}

这里先记住两者的功能,具体后面会分析

注释1

先看下Street_Factory.java

1
2
3
4
5
6
7
8
9
10
public final class Street_Factory implements Factory<Street> {
private static final Street_Factory INSTANCE = new Street_Factory();
@Override
public Street get() {
return new Street();
}
public static Factory<Street> create() {
return INSTANCE;
}
}

代码很简单,通过饿汉式创建了一个Street_Factory单例(这里的源码可能会有不同,之前看过一版是通过枚举创建的单例);再看下City_MembersInjector.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class City_MembersInjector implements MembersInjector<City> {
private final Provider<Street> streetProvider;
public City_MembersInjector(Provider<Street> streetProvider) {
assert streetProvider != null;
this.streetProvider = streetProvider;
}
public static MembersInjector<City> create(Provider<Street> streetProvider) {
return new City_MembersInjector(streetProvider);
}
@Override
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
}

City_MembersInjector只是将传进来的Street_Factory赋值给自己的成员变量;

注释2

后面的功能都一样,就用伪代码列出:

City_Factory.java

1
2
3
4
5
6
public final class City_Factory implements Factory<City> {
public City_Factory(...省略参数){
this.city_MembersInjector = city_MembersInjector;
this.Street_Factory = street_Factory;
}
}

注释3

Province_MembersInjector.java

1
2
3
4
5
public final class Province_MembersInjector implements MembersInjector<Province> {
public Province_MembersInjector(...省略参数){
this.city_Factory = city_Factory
}
}

注释4

Province_Factory.java

1
2
3
4
5
6
public final class Province_Factory implements Factory<Province> {
public Province_Factory(...省略参数){
this.province_MembersInjector = province_MembersInjector
this.city_Factory = city_Factory
}
}

注释5

1
2
3
4
5
6
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Province> provinceProvider;
public MainActivity_MembersInjector(Provider<Province> provinceProvider) {
this.province_Factory = provinceProvider;
}
}

到此目标工厂和注入器都已经创建完成,但是此时目标对象和依赖关系还没产生。

inject()

inject()通过调用injectMembers()完成真正目标对象实例化以及依赖操作,代码也没多少,就把整个完整的过程涉及到的代码贴出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//DaggerMainActivityComponent.java
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}

//MainActivity_MembersInjector.java
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.province = provinceProvider.get();
}

//Province_Factory.java
public Province get() {
return MembersInjectors.injectMembers(
provinceMembersInjector, new Province(cityProvider.get()));
}

//Province_MembersInjector.java
public void injectMembers(Province instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.city = cityProvider.get();
}

//City_Factory.java
public City get() {
return MembersInjectors.injectMembers(cityMembersInjector, new City(streetProvider.get()));
}

//City_MembersInjector.java
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}

//Street_Factory.java
public Street get() {
return new Street();
}

可以看到inject()和initialize()刚好是相反的过程,直到找到依赖的源头完成源头对象的实例化,即这里的Street_Factory的get()方法的返回值;在Street实例化完成之后返回给City完成City实例化,City实例化完之后对自己的成员重新赋值了一遍,即产生依赖关系:

1
2
3
4
5
6
7
/City_MembersInjector.java
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}

instance.street赋值的时候调用了streetProvider.get(),这是一个怎样的过程呢,还是画个图吧

Dagger实例化过程

step1的时候已经完成了Street和City的实例化,接着执行instance.street = streetProvider.get(),这句代码即下面step2的过程:

Dagger实例化过程

红色的就变成垃圾了,所以在这个过程中Street被new了两次,第一次作为创建City的条件,第二次在City创建完成后对自身成员变量赋值;继续分析创建Province以及建立依赖的过程:

Dagger实例化过程

可以看到在整个过程中Street其实new了4次,如果依赖更多的话(比如最外层再加个Country),Street在内存中被new的次数也会*2,不过过程中这些对象最终都会变成垃圾被回收(总觉得这是额外的开销,依赖多了岂不是编译就慢了?),而不用dagger2只要new一次就可以了(如下图),但是两者最终都是一条依赖链

Dagger实例化过程

总结

整个流程:

Dagger实例化和注入流程

黑色的流程线是initialize()的过程,用来创建目标实例的工厂和注入器;红色流程线是inject()/injectMembers()的过程,用来创建目标实例以及实现依赖。最后在回过头来看下@inject和@component这两个标注,可以得出一些结论:

1.若一个类(Xx)的构造函数被@inject标注,则该类编译时会产生Xx_Factory;

2.若一个类中的成员变量被@inject标注,则该类编译时会产生Xx_MembersInjector;

3.@Component标注的接口(Xx)在编译时生成DaggerXx,负责将上面两个联系起来。

换句话说就是,用@inject标注成员属性则标记的是一种依赖关系,而用@inject来标注构造函数则标记的是这种依赖关系的创建构造(只能标记一个构造函数),标记多个构造函数会造成如下错误

1
错误: Types may only contain one @Inject constructor.

依赖注入

但是值得注意的是,构造函数的参数其实和属性不需要一一对应,因为它们没有关系。例如我将Province修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Province {

@Inject
public City city; //City的构造需要标记@Inject

@Inject
public Street street; //Steet的构造需要标记@Inject

@Inject
public Province() {

}

public String showAddress() {
return "四川省" + city.show() + "(" + street.show() + ")";
}
}

输出结果:

1
四川省成都市人民南路(人民南路)

当然dagger2还有很多很强大的功能,之后我们再来看和mvp结构的项目如何整合。

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Babel Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome Class ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage ES2015 ESLint Element Error Exception Extensions File FileProvider Flow Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Style Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code VUE ValueAnimator ViewPropertyAnimator Vue Vue.js Web Web前端 Workbench api apk bookmark by关键字 cli compileOnly component computed css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge methods mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode watch webpack 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指令 指针 插件 插值 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 渲染 源码 源码分析 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 绑定 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 脚手架 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×