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入門到放棄(二)