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

时间:2022-12-19 20:52:00

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

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

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

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

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

  4. 消息队列高手课&comma;带你从源码角度全面解析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. &lbrack;转&rsqb;Android事件分发机制完全解析,带你从源码的角度彻底理解&lpar;上&rpar;

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

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

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

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

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

随机推荐

  1. 【总结】虚拟机VirtualBox各种使用技巧

    作为个人学习研究,VirtualBox是首选,它是Oracle下免费的.开源.跨平台的一款虚拟机软件,小巧.实用,一点也不逊于商业版的VMware Workstation. VirtualBox官网: ...

  2. ubuntu下python 2&period;7与python 3&period;X的转换

    ubuntu下python 2.7与python 3.X的转换 由于ubuntu本身自带python 2.7,而python 3.X与2.7有很多不同,所以在使用python 3.X时会带来诸多不便. ...

  3. webService调用的N种方式

    一.服务器端代码 新建一个web Service Project工程(貌似普通的java工程也可以,这里不太明白各种webService的框架有什么区别),建立如下的类: package com.bo ...

  4. Java运行时环境---ClassLoader类加载机制

    背景:听说ClassLoader类加载机制是进入BAT的必经之路. ClassLoader总述: 普通的Java开发其实用到ClassLoader的地方并不多,但是理解透彻ClassLoader类的加 ...

  5. Android 开源框架Glide的使用

    Glide是一个快速高效的多媒体管理和图像加载的框架,封装了Android平台的多媒体的解码,内存和硬盘缓存等,Glide支持解码.显示视频.图像和GIFs,Glide是基于定制的HttpUrlCon ...

  6. ExtJs5的基本理论概念

    概述 理解ExtJs里面的一些基本关键字的概念是使用ExtJs搭建MMVC框架的基础,在ExtJs中,我们通常遇到ExtJs的配置和启动项Ext.application(),该方法是ExtJs程序初始 ...

  7. opencv学习之路(35)、SURF特征点提取与匹配(三)

    一.简介 二.opencv中的SURF算法接口 三.特征点匹配方法 四.代码 1.特征点提取 #include "opencv2/opencv.hpp" #include < ...

  8. 模拟退火算法-旅行商问题-matlab实现

    整理一下数学建模会用到的算法,供比赛时候参考食用. —————————————————————————————————————————— 旅行商问题(TSP): 给定一系列城市和每对城市之间的距离,求 ...

  9. jdbc问题&colon;Access denied for user &&num;39&semi;&&num;39&semi;&commat;&&num;39&semi;localhost&&num;39&semi;&&num;39&semi;是因为没输入账户和密码

    Access denied for user ''@'localhost' to database 'bjpowernode'

  10. makefile 常用函数

    Linux下编译c/c++源码需要编写makefile文件,文章参看 http://blog.sina.com.cn/s/blog_4c4d6e74010009jr.html 一函数的调用语法 二字符 ...