網(wǎng)上的Dagger2各種多,但看完了,只能讓原本懵逼的更懵逼廉沮,我只能說(shuō)大神的世界我不懂颓遏,那么Dagger2真那么難嗎?給我耐心滞时,我給你答案H薄!坪稽!
1.定義:沒(méi)耐心了解的曼玩,也可以下一步。
Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier versioncreated by Square and now maintained by Google. Dagger aims to address many of the development and performance issues that have plagued reflection-based solutions. More details can be found in this talk(slides) by +Gregory Kick.
什么意思爸习佟黍判?高屋建瓴,一針見(jiàn)血篙梢,力透紙背顷帖,入木三分,高端大氣上檔次渤滞,
- Dagger是為Android和Java平臺(tái)提供的一個(gè)完全靜態(tài)的贬墩,在編譯時(shí)進(jìn)行依賴注入的框架,Dagger解決了基于反射帶來(lái)的開(kāi)發(fā)和性能上的問(wèn)題(因?yàn)镈agger并沒(méi)有用反射來(lái)做依賴注入)就是說(shuō)它幫我們自動(dòng)生成注入需要的代碼妄呕,跟純手寫(xiě)沒(méi)區(qū)別陶舞,只是寫(xiě)這些機(jī)械的代碼的過(guò)程Dagger2 代勞了。
2. 添加依賴
- Project級(jí)別bulid.gradle 添加classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'趴腋,如下
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
allprojects {
repositories {
jcenter()
}
}
-
Module 級(jí)別 build.gradle 頂上位置加apply plugin: 'android-apt',dependencies 里加
provided 'javax.annotation:javax.annotation-api:1.2'
compile 'com.google.dagger:dagger:2.5'
apt 'com.google.dagger:dagger-compiler:2.5'
完成代碼如下:apply plugin: 'com.android.application'
apply plugin: 'android-apt'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.zhang.testing.daggertest"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.0.0'
testCompile 'junit:junit:4.12'
//dagger2
provided 'javax.annotation:javax.annotation-api:1.2'
compile 'com.google.dagger:dagger:2.5'
apt 'com.google.dagger:dagger-compiler:2.5'
}
到這吊说,我們就能在項(xiàng)目中使用Dagger2了
#### 3. 最簡(jiǎn)單的dagger2使用
這里是基于最簡(jiǎn)單的MVP模式的dagger2最簡(jiǎn)單使用。我們先看代碼优炬,這樣效率高颁井,容易理解。不信蠢护,follow me !!!
1. 新建一個(gè)類Prensenter(它的實(shí)例就是商品),,這個(gè)是要注入到MainActivity(MainActivity就是采購(gòu)商)中的雅宾,不懂沒(méi)事,接著往下看葵硕,**這里注意@Inject這個(gè)注解(相當(dāng)于訂單眉抬,Dagger2老司機(jī)就認(rèn)這個(gè))**
```
public class Presenter {
private MainActivity mMainActivity;
@Inject
public Presenter(MainActivity mainActivity) {
this.mMainActivity = mainActivity;
}
//下載數(shù)據(jù)的方法,這里只是模擬
public void loadData() {
System.out.println("下載中");
}
}
- 再新建個(gè)類MainActivityModule(這里比喻為一個(gè)倉(cāng)庫(kù),用@Module表示)Module中有用@Provides注解的方法懈凹,就相當(dāng)于倉(cāng)庫(kù)中的貨架蜀变,可以對(duì)外提供商品。下面代碼中的方法provideMainActivity()方法的返回是MainActivity介评,那么它提供的商品就是MainActivity的實(shí)例
/**
* Created by zyg on 2016/11/8.
*/
@(個(gè)人博客)Module
public class MainActivityModule {
private MainActivity mMainActivity;
public MainActivityModule(MainActivity activity) {
this.mMainActivity = activity;
}
@Provides
public MainActivity provideMainActivity(){
return mMainActivity;
}
}
- 再 新建接口 MainComponent,這個(gè)接口被@Component注解库北,表示它是個(gè)Component(供貨商爬舰,交易商),在注解的后面的括號(hào)里,指明了modules= MainActivityModule.class,將Component 和Module 關(guān)聯(lián)起來(lái)(就是說(shuō)MainActivityModule這個(gè)倉(cāng)庫(kù)寒瓦,是MainComponent這個(gè)供貨商的倉(cāng)庫(kù))
/**
* Created by zyg on 2016/11/8.
*/
@Component(modules = MainActivityModule.class)
public interface MainComponent {
void inject(MainActivity mainActivity);
}
- 再看MainActivity方法情屹,我們的目的就是將 Prensenter的實(shí)例注入到MainActivity中, 這里我們申明了Presenter mPresenter,并且在它的頭上也有個(gè)@Inject標(biāo)注(告訴老司機(jī)Dagger2這是我缺的貨,快去給我從供貨商那給我拉回來(lái)),然后rebuild工程杂腰,注入所需要的代碼就會(huì)自動(dòng)生成垃你。然后我們?cè)僬{(diào)用下面這些方法,完成最后的注入
DaggerMainComponent.builder()
.mainActivityModule(new MainActivityModule(this))
.build()
.inject(this);**
package com.zhang.testing.simplemvpdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.zhang.testing.simplemvpdemo.di.component.DaggerMainComponent;
import com.zhang.testing.simplemvpdemo.di.module.MainActivityModule;
import com.zhang.testing.simplemvpdemo.presenter.Presenter;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "daggerTest";
@Inject
Presenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainActivityModule(new MainActivityModule(this))
.build()
.inject(this);
}
//更新UI的操作
public void refeshUi() {
Log.d(TAG, "更新ui");
}
}
分析總結(jié):到這里一個(gè)最簡(jiǎn)單的dagger2注入過(guò)程就完成了喂很,我們把Presenter 的實(shí)例注入到MainActivity中惜颇,這時(shí)候我們就能調(diào)用Prensent 中的方法了。因?yàn)槭荕vp模式恤筛,所以Prensenter類也需要拿到了MainActivity的實(shí)例官还,(至于怎么拿到的,少年莫急毒坛,一步一步慢慢來(lái)M住)調(diào)用Prensent中的方法完成下載后,就可以調(diào)用MainActivity中的方法完成UI的更新煎殷。
- 下面我們分析下Dagger2老司機(jī)怎么把MainActivity這個(gè)采購(gòu)商從供貨商哪里采購(gòu)到商品的
- 在MainActivity 中的@Inject注解告訴Dagger2這個(gè)貨車?yán)纤緳C(jī)需要采購(gòu)什么了屯伞,我缺什么貨了,我缺P(pán)resenter類的實(shí)例(呵呵豪直,Dagger2這個(gè)老司機(jī)只認(rèn)識(shí)@Inject注解的訂單)
- 然后Dagger2就會(huì)去和MainActivity相關(guān)的供貨商Component去要劣摇,Component就會(huì)去和它關(guān)聯(lián)的Module倉(cāng)庫(kù)中去找,但是打開(kāi)倉(cāng)庫(kù)也是有條件的(管理比較嚴(yán)格)弓乙,就是得有MainActivity的身份證(就是MainAcitivty的實(shí)例末融,,你理解成錢也好暇韧,身份證也罷勾习,反正就是個(gè)憑證),Component在貨架(@Provides注解的方法)中找了半天也沒(méi)有懈玻,只發(fā)現(xiàn)提供MainAcivity的實(shí)例的貨架巧婶。
- 于是Component 只能去找Presenter本人碰碰運(yùn)氣,運(yùn)氣還不錯(cuò)涂乌。Presenter中有@Inject注解的構(gòu)造方法艺栈,這里可以提供。但是Presenter說(shuō)你得給我提供一個(gè) MainActivity的身份證(還是 MainActivity的實(shí)例)湾盒,我才能給你new 一個(gè)Prensenter實(shí)例湿右,巧了,剛在Module倉(cāng)庫(kù)的不是有提供 MainActivity的實(shí)例的貨架罚勾?把MainActivity實(shí)例給了Presenter,Presenter就new了個(gè)實(shí)例給了Component,同時(shí)Prensenter也拿到了 Activity的實(shí)例毅人。
- Component屁顛屁顛的跑回來(lái)和Dagger2老司機(jī)說(shuō)漾唉,拿到Presenter實(shí)例了,但是你得提供MainActivity的身份證(MainActivity的實(shí)例)咱們簽訂合同堰塌,你就把Presenter的實(shí)例拉走,調(diào)用inject方法簽訂合同分衫,Dagger2老司機(jī)就把貨給MainActivity拉回來(lái)了3⌒獭(老司機(jī)就是牛!r秸健GO帧)
- 說(shuō)到這里,整個(gè)注入過(guò)程就完成了邀桑。具體怎么注入瞎疼,一會(huì)我們從生成的源代碼分析,這里先嘗試著理解壁畸。為了生動(dòng)易于理解贼急,以上的分析不代表實(shí)際執(zhí)行過(guò)程,但你這么理解是沒(méi)有問(wèn)題的捏萍。說(shuō)到這里代碼中各個(gè)注解的意思太抓,應(yīng)該已經(jīng)理解了,不過(guò)這里還是提一下
- @Module:表明這個(gè)類是個(gè)Module類(Module倉(cāng)庫(kù))令杈,會(huì)跟Component關(guān)聯(lián)走敌,這個(gè)類中可能有些提供方法可以向外提供一些類的實(shí)例
- @Provides:表明該方法是個(gè)提供方法,這個(gè)方法就是在Module中向外提供實(shí)例的方法(貨架逗噩,提供的實(shí)例就是商品)
- @Component:表明這個(gè)接口是個(gè)Component(供貨商)掉丽,是連接Module(倉(cāng)庫(kù)) 和被注入類(商品),目標(biāo)類(采購(gòu)商)的橋梁异雁,需要注入到哪個(gè)類捶障,在inject方法中,目標(biāo)類的實(shí)例作為參數(shù)傳進(jìn)來(lái)片迅,這里注意下残邀,我們這里的目標(biāo)類是MainActivity,所以參數(shù)就是MainActivity的實(shí)例柑蛇,但是我們不能用MainAcvity的父類接收芥挣,那樣會(huì)報(bào)錯(cuò)的(MainActivity父親的來(lái)都不行)。
4. Dagger2避免了反射對(duì)性能造成的影像耻台,通過(guò)自動(dòng)生成代碼完成空免,注入。現(xiàn)在我們從它生成代碼的角度去看看它到底是怎么完成注入的盆耽,自動(dòng)生成的代碼在\DaggerTest\app\build\generated\source\apt\debug\下面蹋砚。下面看代碼吧6蟛ぁ!
- 我們以MainActivity中的調(diào)用順序來(lái)看坝咐,代碼的走向 **DaggerMainComponent.builder() **我們看這句代碼做了些什么事
public static Builder builder() {
return new Builder();
}
這是個(gè)靜態(tài)方法循榆,new 了一個(gè) Builder的實(shí)例返回,Builder為其內(nèi)部類墨坚,構(gòu)造方法中秧饮,什么也沒(méi)有做。接著看MainActivity中下一步調(diào)用什么泽篮。
- 然后就是調(diào)用Bulider類的mainActivityModule(new MainActivityModule(this))方法盗尸,并且傳入一個(gè)MainActivityModule的實(shí)例作為參數(shù)。而new一個(gè)MainActivityModule的實(shí)例需要傳入MainActivity的實(shí)例帽撑,下面看這個(gè)方法做了什么.
public static final class Builder {
private MainActivityModule mainActivityModule;
private Builder() {}
public MainComponent build() {
if (mainActivityModule == null) {
throw new IllegalStateException(
MainActivityModule.class.getCanonicalName() + " must be set");
}
return new DaggerMainComponent(this);
}
public Builder mainActivityModule(MainActivityModule mainActivityModule) {
this.mainActivityModule = Preconditions.checkNotNull(mainActivityModule);
return this;
}
}
這個(gè)方法泼各,把傳進(jìn)來(lái)的MainActivityModule的實(shí)例被Builder這個(gè)類裝進(jìn)自己兜里了是不是?然后又返回了自己亏拉。
拿到返回的builder實(shí)例扣蜻,MainActivity中繼續(xù)調(diào)用Builder的build()方法,這個(gè)方法首先判斷了下全局變量mainActivityModule专筷,是否為空弱贼,為空就拋出異常。這個(gè)Builder磷蛹,還真是貪婪吮旅,非要確認(rèn)MainActiivtyModule的實(shí)例是不是已經(jīng)裝兜里了,然后就new個(gè)了DaggerMainComponent的實(shí)例返回味咳,并把自己作為參數(shù)傳了進(jìn)去庇勃,注意:這時(shí)候的Buidler可不簡(jiǎn)單,是個(gè)小土豪槽驶,兜里裝著MainActivityModule的實(shí)例责嚷,而MainAcvitivityModule的實(shí)例又有揣著 MainActivity的實(shí)例。
然后我們看下 DaggerMainComponent的new方法里都做了些什么,
public final class DaggerMainComponent implements MainComponent {
private Provider<MainActivity> provideMainActivityProvider;
private Provider<Presenter> presenterProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerMainComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideMainActivityProvider =
MainActivityModule_ProvideMainActivityFactory.create(builder.mainActivityModule);
this.presenterProvider = Presenter_Factory.create(provideMainActivityProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(presenterProvider);
}
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
public static final class Builder {
private MainActivityModule mainActivityModule;
private Builder() {}
public MainComponent build() {
if (mainActivityModule == null) {
throw new IllegalStateException(
MainActivityModule.class.getCanonicalName() + " must be set");
}
return new DaggerMainComponent(this);
}
public Builder mainActivityModule(MainActivityModule mainActivityModule) {
this.mainActivityModule = Preconditions.checkNotNull(mainActivityModule);
return this;
}
}
}
- 在DaggerMainComponent 的new方法里掂铐,它調(diào)用了initialize(builder)罕拂,方法,并把Bulider實(shí)例傳進(jìn)去了全陨,接著看initialize做了什么爆班,
- 第一步它調(diào)用** MainActivityModule_ProvideMainActivityFactory.create(builder.mainActivityModule);**,這個(gè)類名很長(zhǎng)辱姨,我們 就叫MF(Module工廠類)柿菩,調(diào)用了MF 的creat方法,并把builder中的mainActivityModule傳了進(jìn)去作為參數(shù)(這下Bulider的財(cái)富被轉(zhuǎn)移了雨涛,哈哈)MF的creat方法枢舶,new了一個(gè)MF返回懦胞,然后把mainActivityModule裝自己兜里了,都貪財(cái)傲剐埂躏尉!然后DaggerMainComponent 用一個(gè)類接收了MF的實(shí)例給接收了,至于什么類,我們不用去關(guān)心后众,總之肯定是父類超類什么能接受的醇份。下面是MF類的代碼
public final class MainActivityModule_ProvideMainActivityFactory implements Factory<MainActivity> {
private final MainActivityModule module;
public MainActivityModule_ProvideMainActivityFactory(MainActivityModule module) {
assert module != null;
this.module = module;
}
@Override
public MainActivity get() {
return Preconditions.checkNotNull(
module.provideMainActivity(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Factory<MainActivity> create(MainActivityModule module) {
return new MainActivityModule_ProvideMainActivityFactory(module);
}
}
- 第二步Presenter_Factory.create(provideMainActivityProvider);方法,Presenter_Factory.我們把這個(gè)類簡(jiǎn)稱為PF(Presenter工廠類)吼具,調(diào)用PF的creat方法,并把MF的實(shí)例傳進(jìn)去矩距。這時(shí)候的MF也是個(gè)富豪哦拗盒。 PF的creat方法,也沒(méi)什么新意锥债,依然是new一個(gè)PF實(shí)例返回陡蝇,然后把MF實(shí)例裝包包里,大魚(yú)吃小魚(yú)跋恰登夫!DaggerMainComponent,又把PF實(shí)例收了允趟。下面是PF的代碼
public final class Presenter_Factory implements Factory<Presenter> {
private final Provider<MainActivity> mainActivityProvider;
public Presenter_Factory(Provider<MainActivity> mainActivityProvider) {
assert mainActivityProvider != null;
this.mainActivityProvider = mainActivityProvider;
}
@Override
public Presenter get() {
return new Presenter(mainActivityProvider.get());
}
public static Factory<Presenter> create(Provider<MainActivity> mainActivityProvider) {
return new Presenter_Factory(mainActivityProvider);
}
}
- 第三步 this.mainActivityMembersInjector = MainActivity_MembersInjector.create(presenterProvider); MainActivity_MembersInjector這個(gè)類我們簡(jiǎn)稱為MI吧恼策!這里依然是老套路,MI的creat方法潮剪,依然是返回MI的實(shí)例涣楷,然后把PF的實(shí)例裝自己的兜里。DaggerMainComponent 依然是把MI的實(shí)例接收了抗碰。到目前為MI很富裕狮斗,它里面有PF的實(shí)例,而PF里有MF的實(shí)例弧蝇,MF里有MainActivityModule的實(shí)例碳褒,而MainActivityModule中又有MainActivity的實(shí)例,大魚(yú)吃小魚(yú)下雨吃蝦米的關(guān)系啊看疗。下面是MI的代碼
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Presenter> mPresenterProvider;
public MainActivity_MembersInjector(Provider<Presenter> mPresenterProvider) {
assert mPresenterProvider != null;
this.mPresenterProvider = mPresenterProvider;
}
public static MembersInjector<MainActivity> create(Provider<Presenter> mPresenterProvider) {
return new MainActivity_MembersInjector(mPresenterProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mPresenter = mPresenterProvider.get();
}
public static void injectMPresenter(
MainActivity instance, Provider<Presenter> mPresenterProvider) {
instance.mPresenter = mPresenterProvider.get();
}
}
- 這時(shí)候我們已經(jīng)拿到了DaggerMainComponent 的實(shí)例沙峻,再看下MainActivity中,又調(diào)用了DaggerMainComponent 的inject鹃觉,方法专酗,傳入?yún)?shù)MainActivity的實(shí)例。這個(gè)方法又調(diào)用MI的injectMembers方法并把MainActivity的實(shí)例作為參數(shù)傳進(jìn)去了
- 在看Mi的injectMembers方法盗扇,這是個(gè)抽象方法祷肯,具體實(shí)現(xiàn)在MI里沉填,那么我們到Mi里看唄。
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mPresenter = mPresenterProvider.get();
}
MI的injectMembers佑笋,方法把 MainActvity中的mPresenter 取出來(lái)了翼闹,mPresenter 是誰(shuí)?蒋纬?猎荠,我們需要注入到MainActivity中的Presenter類的引用,只要它被實(shí)例化了蜀备,咱們工作基本就完成了关摇,那么再看看等號(hào)的右邊,mPresenterProvider.get()碾阁,mPresenterProvider何許人也输虱?,他就是MI creat方法的時(shí)候傳進(jìn)來(lái)的PF實(shí)例脂凶,被MI包裝了下宪睹,搞的差點(diǎn)不認(rèn)識(shí)。(嘿嘿蚕钦,整容了)亭病。調(diào)用它的get方法,依然是個(gè)抽象方法嘶居,實(shí)現(xiàn)就在PF里罪帖,(僅僅是換個(gè)行頭,所以該P(yáng)F做的事還是PF做坝势ā)
- 在看PF的get方法都干了些什么胸蛛,
@Override
public Presenter get() {
return new Presenter(mainActivityProvider.get());
}
這里PF new了一個(gè)Presenter的實(shí)例并返回了,到這里樱报,等號(hào)的左邊(不要糾結(jié)于概念葬项,你非要說(shuō)在這里它不叫等號(hào),那么我也只能無(wú)語(yǔ))是Presenter的引用迹蛤,右邊是實(shí)例民珍,mPresenter實(shí)例化完成,即注入完成盗飒。這里我們也看到嚷量,new Presenter的時(shí)候需要傳入MainActivity實(shí)例作為參數(shù),這里通過(guò)mainActivityProvider的get方法獲取逆趣,mainActivityProvider是誰(shuí)蝶溶?不用多說(shuō)了吧!就是經(jīng)過(guò)整容的MF,可要認(rèn)的出來(lái)才行。
- get方法抖所,依然是抽象方法梨州,具體實(shí)現(xiàn)在MF里。我們?cè)诳纯碝F的get方法
@Override
public MainActivity get() {
return Preconditions.checkNotNull(
module.provideMainActivity(), "Cannot return null from a non-@Nullable @Provides method");
}
調(diào)用了module的provideMainActivity方法田轧,這 module是誰(shuí)暴匠,不用再說(shuō)了吧,就是MainActivityMoule的實(shí)例傻粘,它的provideMainActivity每窖,方法返回什么,還是我們自己寫(xiě)的呢弦悉。就是返回MainActivity的實(shí)例窒典。通過(guò)一系列的get方法,拿到MainActivity的實(shí)例稽莉,然后傳到Presenter的new方法崇败,這樣Presenter就拿到了MainActivity的實(shí)例。到這里肩祥,所有的注入才算是真正完成。我們的最佳MVP也算是正在完成缩膝。
如果到這里你還不明白混狠,不怕我們還有一招,看圖
這里我就不做過(guò)多解釋了疾层,根據(jù)上面文字描述繪制的圖譜将饺,從new MainActivityModule開(kāi)始,MainActivity的實(shí)例被包裝到 Module里痛黎,然后整個(gè)Module 的實(shí)例又被包裝到 Builder里予弧,然后把Modle取出,再次包裝到 MF里(Modle的工廠類)然后再 在包裝到PF里湖饱,最后又把PF包裝到MI里掖蛤,然后再調(diào)用一系列的get方法取出this,完成注入井厌。