??轉(zhuǎn)發(fā)請(qǐng)注明出處:http://www.reibang.com/p/7ee1a1100fab
??Dagger2作為Android界最具殺傷力的匕首,本系列文章將用最通俗的語言帶領(lǐng)你揭開它的真面目修档。
??邊緣OB:從零單排帶你從低分局打到高分局萧求,從First Blood(第一滴血)到Holy Shit(超越神的殺戮)姻乓,每盤Rampage(暴走)不在話下想幻!
??Android Dagger2 從零單排(一) 基礎(chǔ)注解
??Android Dagger2 從零單排(二) @Qualifier
??Android Dagger2 從零單排(三) @Scope
??Android Dagger2 從零單排(四) Dependencies與SubComponent
1.Dagger2簡(jiǎn)介
??"A fast dependency injector for Android and Java."
??Dagger2GitHub地址的首頁簡(jiǎn)介就這么一句話恋谭,Android和java的快速依賴注入场斑。簡(jiǎn)單述說就是組件只管依賴的使用漓踢,而依賴的具體實(shí)現(xiàn)交給容器去決定,這是DI(Dependency Injection)框架的核心思想漏隐。另外使用Dagger2不用擔(dān)心性能的消耗喧半,不使用反射,所有的注解均停留在編譯時(shí)期青责。
2.配置Dagger2:
dependencies {
api 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}
??注意:如果gradle版本低于2.2挺据,需要使用apt插件,Kotlin寫法也有點(diǎn)脖隶。
3.注解的分類:
??在描述注解的作用前扁耐,我們先看dagger2最簡(jiǎn)單的例子,
??例子一产阱,假設(shè)領(lǐng)導(dǎo)下午要出去視察民情婉称,需要一輛公交車,司機(jī)就不用請(qǐng)了构蹬,領(lǐng)導(dǎo)做表率自己開王暗,小秘把領(lǐng)導(dǎo)的指示告訴了車場(chǎng)調(diào)度員。
??找輛公交車要停放在停車場(chǎng)(Bus要注入到ParkingActivity)庄敛,停車場(chǎng)不管公交車按什么路線停車(ParkingActivity不管Bus是如何注入的)俗壹,車場(chǎng)調(diào)度員會(huì)負(fù)責(zé)好管理(Dagger2容器會(huì)將Bus的注入到ParkingActivity)。
??DaggerParkingComponent類是編譯過后Dagger2自動(dòng)生成的藻烤,是ParkingComponent的實(shí)現(xiàn)類绷雏,可以說DaggerParkingComponent就是實(shí)際的車場(chǎng)調(diào)度員,ParkingComponent是對(duì)車場(chǎng)調(diào)度員的約束怖亭,在onCreate方法完成了注入過程之众。
public class Bus {
@Inject
public Bus() {
}
}
public class ParkingActivity extends Activity {
@Inject
Bus mBus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
DaggerParkingComponent.create().inject(this);//DaggerParkingComponent類需要編譯才會(huì)生成
((TextView) findViewById(R.id.text)).setText(mBus.toString());
}
}
@Component
public interface ParkingComponent {
void inject(ParkingActivity activity);
}
@Inject注解:
??(1)注解在屬性中表示該屬性需要依賴注入,不能使用private修飾依许,示例代碼表示需要注入屬性mBus(Bus的車位):
@Inject
Bus mBus;
??(2)注解在方法中表示該方法需要依賴注入棺禾,不能是抽象方法,不能使用private修飾峭跳,示例代碼表示需要注入方法injectBus:
//@Inject
Bus mBus;
@Inject
public void injectBus(Bus bus) {
mBus = bus;
}
??方法注入的參數(shù)同樣由Dagger2容器提供膘婶,以上代碼的目的與第一點(diǎn)介紹的屬性注入一樣缺前,都是為了注入mBus,如果目的是注入屬性的話悬襟,方法注入和屬性注入基本沒有區(qū)別衅码,屬性注入是Dagger2中使用最多的一個(gè)注入方式。
??那么什么情況下應(yīng)該使用方法注入脊岳?比如依賴需要this對(duì)象的時(shí)候逝段,方法注入可以提供安全的this對(duì)象。
??注意割捅,Dagger2容器先調(diào)用屬性注入奶躯,然后再方法注入,若把示例代碼mBus的@Inject取消注釋亿驾,此時(shí)mBus會(huì)注入2次嘹黔,并且兩次注入的Bus也不相同。
??(3)注解在構(gòu)造方法中表示此類能為Dagger2提供依賴關(guān)系莫瞬,Dagger2可以使用這個(gè)構(gòu)造方法構(gòu)建對(duì)象(Bus的來歷):
@Inject
public Bus() {
}
??如果有多個(gè)構(gòu)造函數(shù)儡蔓,只能注解一個(gè),否則編譯報(bào)錯(cuò)疼邀,多構(gòu)造方法的方式下一篇會(huì)介紹喂江。
@Component注解:
??一般用來注解接口,被注解的接口在編譯時(shí)會(huì)生成相應(yīng)的實(shí)例旁振,實(shí)例名稱一般以Dagger為前綴开呐,作為所需注入依賴(ParkingActivity的mBus屬性)和提供依賴(Bus類構(gòu)造方法)之間的橋梁,把提供的依賴(Bus)注入到所需注入的依賴中(ParkingActivity的mBus屬性)规求。
??通俗說,就是Dagger2的容器卵惦,在例子中是車場(chǎng)調(diào)度員阻肿,把公交車和停車場(chǎng)聯(lián)系在一起。車場(chǎng)調(diào)度員知道停車場(chǎng)要準(zhǔn)備一輛公交車沮尿,停車場(chǎng)不需要知道車是哪里來的丛塌,也不需要知道怎么停,車場(chǎng)調(diào)度員找好車后畜疾,停在車位上就完事赴邻,等候領(lǐng)導(dǎo)用車就可以了。
??例子一總結(jié)啡捶,兩個(gè)@Inject注解形成了依賴關(guān)系姥敛,@Component作為連接這個(gè)關(guān)系的橋梁存在,尋找到依賴并且注入瞎暑,并且注入與被注入之間互不干涉彤敛,經(jīng)過編譯@Component生成Dagger為前綴的實(shí)例与帆,調(diào)用實(shí)例的方法注入。
??例子二墨榄,領(lǐng)導(dǎo)想著不妥玄糟,自己沒開過公交車掂器,為保險(xiǎn)起見還是趕緊吩咐小秘刻像,去給配個(gè)公交車司機(jī)宝恶,小秘自然跟車場(chǎng)調(diào)度員說了拥诡。
??車場(chǎng)調(diào)度員一想播歼,只是比之前多了一個(gè)步驟佣耐,給公交車配置一個(gè)司機(jī)(Bus類添加String類型Driver構(gòu)造方法)比然。于是打電話給開公交車的隔壁老王舔庶,老王自然是應(yīng)諾了下來猪狈,于是乎箱沦,對(duì)例子一的代碼做一下修改。
public class Bus {
private String driver;
@Inject
public Bus(String driver) {
this.driver = driver;
}
}
public class ParkingActivity extends Activity {
@Inject
Bus mBus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
DaggerParkingComponent.create().inject(this);//DaggerParkingComponent類需要編譯才會(huì)生成
((TextView) findViewById(R.id.text)).setText(mBus.toString());//重寫B(tài)us的toString()方法能看到打印出"隔壁老王"雇庙,注入成功
}
}
@Component(modules = ParkingModule.class)
public interface ParkingComponent {
void inject(ParkingActivity activity);
}
@Module
public class ParkingModule {
public ParkingModule() {
}
@Provides
public String provideDriver() {
return "隔壁老王";
}
}
@Module注解:
??該注解與@Provides結(jié)合為Dagger2提供依賴關(guān)系谓形,對(duì)上文@Inject第三點(diǎn)的補(bǔ)充,用于不能用@Inject提供依賴的地方疆前,如第三方庫(kù)提供的類寒跳,基本數(shù)據(jù)類型等不能修改源碼的情況。
@Provides注解:
??@Provides僅能注解方法竹椒,且方法所在類要有@Module注解童太。注解后的方法表示Dagger2能用該方法實(shí)例對(duì)象提供依賴。按照慣例胸完,@Provides方法的命名以provide為前綴书释,方便閱讀管理。
??例子二總結(jié)赊窥,首先@Component注解包含了一個(gè)ParkingModule類爆惧,表示Dagger2可以從ParkingModule類查找依賴,Dagger2會(huì)自動(dòng)查找ParkingModule類有@Provides注釋的方法實(shí)例依賴锨能,最后完成注入
??注意1扯再,如果在ParkingModule里面同樣提供Bus的依賴,Dagger2會(huì)優(yōu)先在@Module注解的類上查找依賴址遇,沒有的情況才會(huì)去查詢類的@Inject構(gòu)造方法熄阻,如下面的代碼,則Bus的司機(jī)就是小王而不是老王了倔约。
@Module
public class ParkingModule {
public ParkingModule() {
}
@Provides
public String provideDriver() {
return "隔壁老王";
}
@Provides
public Bus provideBus() {
return new Bus("樓上小王");
}
}
??注意2秃殉,@Module類可以從構(gòu)造方法傳入依賴,@Provides方法也可以有依賴關(guān)系。
??@Provides方法也有依賴關(guān)系的情況复濒,Dagger2會(huì)繼續(xù)查找可以提供依賴的方法脖卖,類似于一種遞歸的狀態(tài),一步一步返回實(shí)例巧颈。如下代碼畦木,ParkingModule構(gòu)造方法傳入driver為provideDriver方法提供依賴返回,provideDriver返回driver作為provideBus方法的依賴實(shí)例Bus砸泛。
??ParkingModule加入有參構(gòu)造方法后十籍,調(diào)用方式也需要變成,現(xiàn)在司機(jī)就變成了樓下老李了唇礁。
@Module
public class ParkingModule {
private String driver;
public ParkingModule(String driver) {
this.driver = driver;
}
@Provides
public String provideDriver() {
return driver;
}
@Provides
public Bus provideBus(String driver) {
return new Bus(driver);
}
}
//調(diào)用方式改變
DaggerParkingComponent.builder().parkingModule(new ParkingModule("樓下老李")).build().inject(this);
關(guān)于注入方法的疑惑:
??初次接觸Dagger2時(shí)勾栗,會(huì)發(fā)現(xiàn)每次Build之后,create()方法時(shí)有時(shí)無盏筐,其實(shí)create()方法是簡(jiǎn)化注入流程而設(shè)計(jì)的围俘,點(diǎn)進(jìn)去看源碼實(shí)際上是在內(nèi)部幫你調(diào)用了。
public static MainComponent create() {
return new Builder().build();
}
以下情況都會(huì)生成create()方法:
??(1)@Component注解接口沒有聲明任何Module琢融,這種情況自然不必多說界牡。
??(2)@Component注解接口聲明了Module,沒有使用Module提供的依賴漾抬,無論Module構(gòu)造方法是否有參宿亡,這種情況@Component注解生成的Java文件也有提示:
/**大約意思就是你聲明了Module,但component沒有用到里面的實(shí)例纳令,實(shí)例Module是多余無用的操作挽荠。
* @deprecated This module is declared, but an instance is not used in the component. This
* method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
??(3)@Component注解接口聲明了Module,使用了Module提供的依賴平绩,Module構(gòu)造方法沒有參數(shù)圈匆,點(diǎn)進(jìn)去看源碼實(shí)際上是在內(nèi)部初始化了Module。
public MainComponent build() {
if (mainModule == null) {
this.mainModule = new MainModule();
}
return new DaggerMainComponent(this);
}
下面這種是一定不會(huì)有create()方法出現(xiàn):
??(1)@Component注解接口聲明了Module捏雌,使用了Module提供的依賴跃赚,Module構(gòu)造方法有參數(shù),無論你是否使用了該參數(shù)作為依賴腹忽,這時(shí)候注入都需要初始化Module才能注入成功,看源碼如果不初始化Module會(huì)直接報(bào)異常砚作。
public MainComponent build() {
if (mainModule == null) {
throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set");
}
return new DaggerMainComponent(this);
}
??開始接觸時(shí)create()方法可以作為檢測(cè)手段窘奏,例如聲明了Module,且Module構(gòu)造方法有參數(shù)葫录,如果有create()方法着裹,表明此時(shí)沒使用Module提供的依賴,可以在Module查找下原因米同。實(shí)際項(xiàng)目運(yùn)用中骇扇,create()方法比較少使用摔竿,一般Module都會(huì)傳參。
最后總結(jié)了一下依賴注入流程:
1:查找Module中是否有該實(shí)例的@Provides方法少孝。
??1.1:有继低,走第2點(diǎn)。
??1.2:沒有稍走,查找該實(shí)例是否有@Inject構(gòu)造方法袁翁。
????1.1.1:有,走第3點(diǎn)婿脸。
????1.1.2:沒有粱胜,注入失敗。
2:@Provides方法是否有參數(shù)
??2.1:有狐树,則回到第1點(diǎn)查找每個(gè)參數(shù)的依賴
??2.2:沒有焙压,實(shí)例該類返回一次依賴
3:@Inject構(gòu)造方法是否有參數(shù)
??3.1:有,則回到第1點(diǎn)查找每個(gè)參數(shù)的依賴
??3.2:沒有抑钟,實(shí)例該類返回一次依賴
4:以上流程遞歸返回注入目標(biāo)的所有依賴涯曲,最后依賴注入。
??Demo源碼截我 對(duì)應(yīng)daggerOne包名
??Dagger2 GitHub地址
??Dagger2 官網(wǎng)地址
??所有的測(cè)試實(shí)例均基于2.15版本味赃。
??下一篇掀抹,我們來研究多構(gòu)造方法與多個(gè)@Provides方法返回同一數(shù)據(jù)類型的情況:@Qualifier注解。