17611538698
webmaster@21cto.com

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

资讯 0 1971 2017-04-17 12:00:56
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,和大家一起从源码入坑到跳坑,最后,还有一个很关键事忘说了:最近打算辞职回北京,有没有大神同学推荐工作,带带小弟啊

评论