1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了。

2,接入项目

在项目的Gradle添加如下代码

dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
// 添加android-apt 插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

  在app中的Gradle中添加导入

    // 应用插件
apply plugin: 'com.neenbedankt.android-apt' // dagger 2 的配置
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
compile 'org.glassfish:javax.annotation:10.0-b28'// 添加java 注解库

  简单的注解标签的介绍,由于之前写过一篇博客和大家思考过为什么使用Dagger2(它这么难使用为什么还要使用!!),并且还有一些常见注解标签的使用,所以这里就不和大家废话这么引入了,直接上干货

@Inject Inject主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖,另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖。

@Provide 用Provide来标注一个方法,该方法可以在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Injection的变量赋值。provide主要用于标注Module里的方法

@Module 用Module标注的类是专门用来提供依赖的。有的人可能有些疑惑,看了上面的@Inject,需要在构造函数上标记才能提供依赖,那么如果我们需要提供的类构造函数无法修改怎么办,比如一些jar包里的类,我们无法修改源码。这时候就需要使用Module了。Module可以给不能修改源码的类提供依赖,当然,能用Inject标注的通过Module也可以提供依赖

@Component Component一般用来标注接口,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。

@Singleton  该注解就是通过@scope定义的注解,一般用于提供全局单例。

   现在在实际场景中有这种情况,存在学生在老师的课堂上上课,那么一个学生可能存在多个老师,而一个老师也可能存在多个学生。这里我们为了方便解释,就直接转换成1对1的模式即一个学生只有一个老师,一个老师只有一个学生,然我们来看看转换成我们的java对象是什么样的,首先创建学生实体类

  Student.java

public class Student {
private int id;
private String name;
private Course[] course; public Student() {
System.out.println("Student create!!!");
} public Student(int id, String name, Course[] course) {
this.id = id;
this.name = name;
this.course = course;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Course[] getCourse() {
return course;
} public void setCourse(Course[] course) {
this.course = course;
} public void startLessons() {
System.out.println("开始上课了");
}
}

  我们的学生有一些基本的属性,如id、姓名、所学的课程,还有上课的动作。再看看我们创建老师类

public class Teacher {
Student student; public Teacher() {
student = new student();
} public void teacher() {
student.startLessons();
}
}

  这里我们就有一个问题了,我们老师类中需要一个学生的对象,而按照以前的方法我们肯定是在老师的构造方法中传递学生对象,毫无疑问肯定是new Student创建学生对象的,这里我们要使用Dagger2来代替(至于为什么要使用Dagger2代替,和代替之后有什么好处我以前写过,这里就不和大家废话了)

  首先在确定是我们Teacher类中的student属性需要Student对象,所以添加@Inject注解标签

    @Inject
Student student;

  而我们Dagger2要知道到底我是调用那个构造函数来创建Student对象啊,所以要在Student类中构造函数中添加@Inject标签,标明你Dagger2是要在这个构造方法里面创建的

public class Student {
private int id;
private String name;
private Course[] course; @Inject
public Student() {
System.out.println("Student create!!!");
} public Student(int id, String name, Course[] course) {
this.id = id;
this.name = name;
this.course = course;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Course[] getCourse() {
return course;
} public void setCourse(Course[] course) {
this.course = course;
} public void startLessons() {
System.out.println("开始上课了");
}
}

  然后我们 Student中对象被创建了,而我们Teacher中需要这个student对象,这时候我们差一个桥梁来链接这两个类,从而让我们被创建的student对象运送到需要它的地方,所以现在我们需要使用@Component标签,来修饰我们自定义的一个接口,创建TeacherComponent接口,并使用@Component注解标签修饰,创建inject方法(这里很多看过不少Dagger2文章的同学坑定会有疑问,这里的inject是固定方法名吗,能不能使用injectA啊之类的,先把问题留着,我们后面再讲)

package com.qianmo.rxjavatext;

import dagger.Component;

/**
* Created by Administrator on 2017/4/20 0020.
* E-Mail:543441727@qq.com
*/
@Component
public interface TeacherComponent {
void inject(Teacher teacher);
}

  再在我们Teacher类中初始化

 public Teacher() {
DaggerTeacherComponent.builder().build().inject(this);
}

  这时候可能有同学又有疑问了,DaggerTeacherComponent这个类怎么报错啊 ,其实这个类是编译时产生的类大家不用慌,把代码写完了crtl+F9编译一下就可以了,但是!!!这里DaggerTeacherComponent的书写方法是Dagger加上你前面自定义的Component接口类的类名,一定要注意,不然很多同学都会在这翻车(我曾经在这儿翻出无数次),然后再说一下我们上一个问题 TeacherComponent 中的inject方法是固定方法吗?很明显这里调用的是inject(this);所以不是固定方法,只不过你Component接口类写成injectA(Teacher teacher),那么你这里调用的方法就是injectA(this)

  ok,上面这些都写好了,我们在Teacher类中添加测试类来测试测试

public class Teacher {
//想持有学生对象
@Inject
Student student; public Teacher() {
DaggerTeacherComponent.builder().build().injectA(this);
} public void teacher() {
student.startLessons();
} public static void main(String[] args) {
new Teacher().teacher();
}
}

  看一下打印效果

Student create!!!
开始上课了

3,源码分析

  ok,没什么问题,那我们现在只是停留在会用的阶段,底层我们的源码到底是怎么吧我们的对象创建出来的,还有怎么将我们的对象设置到需要它的地方呢,不要慌,老司机现在就带你来看看源码是什么实现的。这里的源码很简单,一共涉及到三个类,都是我们运行时生成的DaggerTeacherComponent、Teacher_MembersInjector、Student_Factory。

  先来看看DaggerTeacherComponent,按照字面意思这是我们Teacher的桥梁类,源码如下:

package com.qianmo.rxjavatext;

import dagger.MembersInjector;
import javax.annotation.Generated; @Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public final class DaggerTeacherComponent implements TeacherComponent {
private MembersInjector<Teacher> teacherMembersInjector; private DaggerTeacherComponent(Builder builder) {
assert builder != null;
initialize(builder);
} public static Builder builder() {
return new Builder();
} public static TeacherComponent create() {
return builder().build();
} @SuppressWarnings("unchecked")
private void initialize(final Builder builder) { this.teacherMembersInjector = Teacher_MembersInjector.create(Student_Factory.create());
} @Override
public void injectA(Teacher teacher) {
teacherMembersInjector.injectMembers(teacher);
} public static final class Builder {
private Builder() {} public TeacherComponent build() {
return new DaggerTeacherComponent(this);
}
}
}

  我们首先来看我们之前的调用方法如下

DaggerTeacherComponent.builder().build().injectA(this);

  首先看一下DaggerTeacherComponent.builder()方法,我们从源码中可以看到DaggerTeacherComponent.builder()调用生成了一个Builder 对象,我们继续代用builder.builder()方法,而我们的build类中代码如下(这里再次吐槽博客园的编辑器,在线编辑的时候无法显示引用代码的行号,这样我们就没法通过行号来解释每一行代码的意思,而必须要重写贴一次代码,操蛋!!!):

 public static final class Builder {
private Builder() {} public TeacherComponent build() {
return new DaggerTeacherComponent(this);
}
}

  “new DaggerTeacherComponent(this);” 看到没,实际上是调用我们的DaggerTeacherComponent对象,而我们的DaggerTeacherComponent构造函数是调用的initialize()方法,看一下方法里面具体代码

@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.teacherMembersInjector = Teacher_MembersInjector.create(Student_Factory.create());
}

  这时候出现了Teacher_MembersInjector、Student_Factory类,我们先不要管,继续往下看,上面我们调用完builder.builder()方法方法之后,再调用injectA(this)的方法,具体代码如下:

@Override
public void injectA(Teacher teacher) {
teacherMembersInjector.injectMembers(teacher);
}

  呃,这里我们又看到Teacher_MembersInjector这个类对象了,所以这时候看看我们Teacher_MembersInjector的源码了,我们上面一共在两个地方使用了Teacher_MembersInjector,一个是.create方法,一个是injectMember方法,所以我们主要要留心源码里面这两个类,具体源码如下:

package com.qianmo.rxjavatext;

import dagger.MembersInjector;
import javax.annotation.Generated;
import javax.inject.Provider; @Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public final class Teacher_MembersInjector implements MembersInjector<Teacher> {
private final Provider<Student> studentProvider; public Teacher_MembersInjector(Provider<Student> studentProvider) {
assert studentProvider != null;
this.studentProvider = studentProvider;
} public static MembersInjector<Teacher> create(Provider<Student> studentProvider) {
return new Teacher_MembersInjector(studentProvider);
} @Override
public void injectMembers(Teacher instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.student = studentProvider.get();
} public static void injectStudent(Teacher instance, Provider<Student> studentProvider) {
instance.student = studentProvider.get();
}
}

  先看看.create方法,可以看到就是很简单的new Teacher_MembersInjector ,相当于初始化了一些成员变量studentProvider ,至于studentProvider 是什么,从字面的意思上来看是我们student的提供者,那么是时候我们就再来看看Student_Factory.create()方法了,因为是这个类提供了studentProvider 对象

package com.qianmo.rxjavatext;

import dagger.internal.Factory;
import javax.annotation.Generated; @Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public enum Student_Factory implements Factory<Student> {
INSTANCE; @Override
public Student get() {
return new Student();
} public static Factory<Student> create() {
return INSTANCE;
}
}

  窝草,请看get方法中的 “new Student()”代码,这不就是我们的student对象的创建嘛,原来你在这里,再看一下在哪里调用我们的studentProvider.get方法拿到创建的Student对象,这时候请看Teacher_MembersInjector.injectMembers()方法,具体代码如下:

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

  看到我们倒数第二行了没,在这里我们拿到teacher对象,设置它的成员变量student。ok,到这里我们就懂了,整个逻辑就全部通了,首先DaggerTeacherComponent.builder().build()执行完在Student_Factory中创建好了student对象,然后在调用injectA(this)方法,将创建的student对象放置到teacher对象中的student属性上。

  OK,到这里我们就简单的了解了Dagger2的使用了,写到这里篇幅已经比较长了,所以我打算用三篇文章来和大家吃透Dagger2,和大家一起从源码入坑到跳坑,最后,还有一个很关键事忘说了:最近打算辞职回北京,有没有大神同学推荐工作,带带小弟啊.

  下一篇:Android -- 带你从源码角度领悟Dagger2入门到放弃(二)

Android -- 带你从源码角度领悟Dagger2入门到放弃(一)的更多相关文章

  1. Android -- 带你从源码角度领悟Dagger2入门到放弃

    1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...

  2. Android -- 带你从源码角度领悟Dagger2入门到放弃(二)

    1,接着我们上一篇继续介绍,在上一篇我们介绍了简单的@Inject和@Component的结合使用,现在我们继续以老师和学生的例子,我们知道学生上课的时候都会有书籍来辅助听课,先来看看我们之前的Stu ...

  3. Android -- 带你从源码角度领悟Dagger2入门到放弃(三)

    1, 前面两篇文章我们知道了怎么使用常用的四种标签,现在我们结合我们自己的项目中去简单的使用 在我们搭建项目的时候,一般会创建自己的Application,在里面进行一些初始化如一些第三方的Green ...

  4. 消息队列高手课,带你从源码角度全面解析MQ的设计与实现

    消息队列中间件的使用并不复杂,但如果你对消息队列不熟悉,很难构建出健壮.稳定并且高性能的企业级系统,你会面临很多实际问题: 如何选择最适合系统的消息队列产品? 如何保证消息不重复.不丢失? 如果你掌握 ...

  5. Android 带你从源码的角度解析Scroller的滚动实现原理

    转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273),请尊重他人的辛勤劳动成果,谢谢! 今天给大 ...

  6. Android AsyncTask完全解析,带你从源码的角度彻底理解

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11711405 我们都知道,Android UI是线程不安全的,如果想要在子线程里进 ...

  7. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  8. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

  9. [学习总结]7、Android AsyncTask完全解析,带你从源码的角度彻底理解

    我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴 ...

随机推荐

  1. WebSocket学习与使用

    1.WebSocket是什么 WebSocket是一种在单个TCP连接上进行全双工通信的协议,其目的是在浏览器和服务器之间建立一个不受限的双向通信的通道,使得服务器可以主动发送消息给浏览器.在HTML ...

  2. CPU高速缓存行与内存关系 及并发MESI 协议

    先来一个整体图 一. 大致关系: CPU Cache --> 前端总线 FSB (下图中的Bus) --> Memory 内存 CPU 为了更快的执行代码.于是当从内存中读取数据时,并不是 ...

  3. TeamViewer 版本v13.2.26558 修改ID

    TeamViewer 使用频繁后会被判定为商业用途,不可用.此软件的账号和设备mac地址绑定. 修改TeamViewer ID后可以重新开始使用.下述方法可以成功修改TeamViewer ID. Wi ...

  4. python 多文件知识

    对于一个大型的项目,会存在很多个py文件,本文记录与多文件有关的内容. 1. python 如何在一个.py文件中调用另一个.py文件的类 如果是在同一个 module中(也就是同一个py 文件里), ...

  5. Chrome Google浏览器下载

    https://support.google.com/chrome/answer/95346?co=GENIE.Platform%3DDesktop&hl=zh-Hans    下载和安装 G ...

  6. CF 977E Cyclic Components

    E. Cyclic Components time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  7. Golang, 以 9 个简短代码片段,弄懂 defer 的使用特点

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  8. 11.4vue(3)

    2018-11-4 20:57:28 越努力越幸运!永远不要高估自己!!!!! 要主动学习!!! 快把vue看完,进行路飞项目!明天整理一下vue知识点!

  9. 31、cookie小test

    请尽量使用JQuery进行代码编写,需求如下: 1.  页面初始化样式如图 2. 顶部input框中输入内容,按下回车enter键后,“正在进行” 列表中加入该条内容.   3. 顶部input框中输 ...

  10. 用TreeSet生成不重复自动排序随机数组

    随机数组就是在指定长度的数组中用随机数字为每个元素赋值,常用于不确定数值的环境,如拼图游戏需要随机数组来打乱图片顺序.可是同时也存在问题,就是随机数的重复问题,这个问题常常被忽略. TreeSet类的 ...