前言
本篇文章將結(jié)合我自己寫(xiě)的咖啡店賣咖啡的栗子的demo,結(jié)合代碼來(lái)分析一下Dagger2是如何來(lái)實(shí)現(xiàn)依賴注入的逾条。
在我的第一篇文章中有提到如何配置Dagger2以及Dagger2的幾個(gè)關(guān)鍵概念涝登,不清楚怎么配置的你有额,建議可以先看看我的第一篇文章癣猾,先配置好Dagger2再開(kāi)始學(xué)習(xí)下面的內(nèi)容晰骑,由于結(jié)合代碼适秩,大部分代碼都貼出來(lái)的,篇幅較長(zhǎng),做好心理準(zhǔn)備秽荞,前方預(yù)警骤公。
總是想著可以開(kāi)一家咖啡店,現(xiàn)在現(xiàn)實(shí)中開(kāi)不了扬跋,那就現(xiàn)在代碼里開(kāi)一家吧阶捆。程序員不僅能宅出一個(gè)世界,更重要的你能創(chuàng)建出一個(gè)你自己的世界钦听,是不是突然自己好厲害洒试! 好吧,不扯了彪见,回到現(xiàn)實(shí)中儡司,好好學(xué)習(xí)吧。
我先創(chuàng)建一家銷售咖啡的咖啡店余指,然后在里面買各種咖啡捕犬,黑咖啡呀等。既然是咖啡店酵镜,那肯定要有咖啡呀碉碉。所以咖啡就是我需要的一個(gè)對(duì)象。所以我的咖啡店還需要依賴于咖啡淮韭,沒(méi)咖啡垢粮,那怎么行,準(zhǔn)備好票子靠粪,來(lái)買我的咖啡吧蜡吧。根據(jù)這個(gè)需求,我的咖啡店類如下:
public class CoffeeShopActivity extends AppCompatActivity {
// 1. 我要賣的咖啡對(duì)象占键,用inject注明要依賴的對(duì)象
@Inject
Coffee mCoffee ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inject();
operate();
}
//2.添加依賴關(guān)鍵代碼昔善。
private void inject() {
DaggerCoffeeShopComponent.builder().build().inject(this);
}
private void operate() {
mCoffee.getCoffee();
}
}
Coffee是一個(gè)很簡(jiǎn)單的類,里面提供了一個(gè)getCoffee方法畔乙,打印了一串信息而已君仆。
public class Coffee {
private static final String TAG = Coffee.class.getSimpleName();
@Inject
public Coffee(){
}
public void getCoffee() {
Log.i(TAG, "black coffee");
}
}
咖啡店里面是個(gè)很簡(jiǎn)單的依賴關(guān)系,CoffeeShopActivity依賴于類Coffee牲距,可以看到我們沒(méi)有用 new
來(lái)創(chuàng)建Coffee對(duì)象返咱,而是直接通過(guò)@Inject來(lái)標(biāo)明CoffeeShopActivity的mCoffee需要依賴注入,來(lái)實(shí)例化這個(gè)變量牍鞠。是不是很神奇,如果你知道了依賴注入是怎么注入的之后咖摹,你就不會(huì)覺(jué)得很神奇了。下面我們一層層地來(lái)剝開(kāi)這個(gè)“洋蔥”吧皮服。
首先在上面代碼里楞艾,成員變量mCoffee定義的時(shí)候参咙,添加了@Inject注釋,然后Coffee的構(gòu)造函數(shù)添加了@Inject注釋硫眯。這樣就CoffeeShopActivity告訴了Dagger2蕴侧,我需要Coffee的實(shí)例,然后Dagger2就去找到Coffee的構(gòu)造函數(shù)創(chuàng)建一個(gè)Coffee两入。難道就只要這兩個(gè)類就可以了嗎净宵,之前不是說(shuō)Component是目標(biāo)類與被注入類之間的橋梁?jiǎn)幔?Component呢? 沒(méi)錯(cuò)裹纳,我還沒(méi)貼出Componet呢择葡。自定義個(gè)Componet類如下:
@Component
public interface CoffeeShopComponent {
void inject(CoffeeShopActivity coffeeShopActivity);
}
這個(gè)Component是不是很簡(jiǎn)單,只有一個(gè)抽象函數(shù)剃氧,方法名是inject敏储,方法名不是必須是inject,也可以是其他名字朋鞍,但是參數(shù)一定要是你的目標(biāo)類已添,而且注意哦,不能是Activity滥酥,一定要是目標(biāo)類自己的類名更舞。在第一篇文章里面就有提到過(guò),這里再?gòu)?qiáng)調(diào)一下坎吻,Dagger框架是通過(guò)參數(shù)的類型以及返回值類型缆蝉,找到要依賴的類,從而建立聯(lián)系的瘦真,所以類名一定要是直接要注入依賴的目標(biāo)類刊头,返回值也是要注入的類。
好了诸尽,注射器(Component)寫(xiě)好了芽偏,那要派上用場(chǎng)呀!那是在哪里用注射器把依賴注入了MainActivity呢弦讽。請(qǐng)看,我在MainActivity里面第2個(gè)注釋膀哲,inject()方法往产。沒(méi)錯(cuò)就是這個(gè)方法,在這個(gè)方法里面用DaggerCoffeeShopComponent.builder().build().inject(this);
來(lái)實(shí)現(xiàn)了注入某宪。 等等仿村,這個(gè)DaggerCoffeeShopComponet
是哪里來(lái)的? 大家還記得在配置Dagger2框架環(huán)境的時(shí)候兴喂,引入了APT的包嗎蔼囊,這個(gè)包是一個(gè)gradle插件焚志,用于結(jié)合注釋自動(dòng)生成代碼,所以這個(gè)包一定要配置畏鼓,否則不能自動(dòng)根據(jù)Dagger2的注釋來(lái)生成關(guān)鍵的代碼酱酬。想要了解APT的可以看看這篇文章android-apt。 所以DaggerCoffeeShopComponent就是一個(gè)自動(dòng)生成的實(shí)現(xiàn)了CoffeeShopComponent的類云矫。文件路徑是moudle路徑\build\generated\source\apt\debug\com\gotech\changelauncherbg\demodagger\DaggerCoffeeShopComponent
膳沽,下面自動(dòng)生成的類都在這個(gè)路徑里面。當(dāng)然APT還根據(jù)注釋自動(dòng)生成了其他的代碼让禀。我們先分析這個(gè)挑社,因?yàn)檫@個(gè)是實(shí)現(xiàn)依賴注入的入口類。注意啦巡揍,自動(dòng)生成代碼痛阻,是要你執(zhí)行了rebuild project
才會(huì)自動(dòng)生成的,所以呢你編寫(xiě)完代碼之后記得一定要rebuild
才能得到這些類腮敌。
好吧阱当,下面就一起來(lái)看看DaggerCoffeeShopComponent是怎么把Coffee的實(shí)例,注入到mCoffee里面的缀皱。上代碼:
//說(shuō)明這段代碼時(shí)生成的斗这,并且生成自ComponetProcessor
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerCoffeeShopComponent implements CoffeeShopComponent {
private MembersInjector<CoffeeShopActivity> coffeeShopActivityMembersInjector;
private DaggerCoffeeShopComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static CoffeeShopComponent create() {
return builder().build();
}
// 初始化
private void initialize(final Builder builder) {
this.coffeeShopActivityMembersInjector = CoffeeShopActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), Coffee_Factory.create());
}
//重寫(xiě)inject方法,實(shí)現(xiàn)依賴注入
@Override
public void inject(CoffeeShopActivity coffeeShopActivity) {
coffeeShopActivityMembersInjector.injectMembers(coffeeShopActivity);
}
public static final class Builder {
private Builder() {
}
public CoffeeShopComponent build() {
return new DaggerCoffeeShopComponent(this);
}
}
}
從上面的代碼可以知道啤斗,DaggerCoffeeComponent
用有一個(gè)內(nèi)部建造類Builder表箭,可以看成一個(gè)簡(jiǎn)單的建造模式的應(yīng)用。利用Builder來(lái)建造一個(gè)DaggerCoffeeShopComponet
對(duì)象然后利用CoffeeShopActivity_MembersInjector
初始化coffeeShopActivityMembersInjector
钮莲。這個(gè)是真正實(shí)現(xiàn)依賴注入的類免钻。從類名MembersInjector<CoffeeShopActivity>
可以看出這是一個(gè)用于注入成員變量的注入器,然后要注入的目標(biāo)類是CoffeeShopActivity
崔拥。 接下來(lái)极舔,我們看看CoffeeShopActivity_MembersInjector
這個(gè)類的代碼。
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class CoffeeShopActivity_MembersInjector implements MembersInjector<CoffeeShopActivity> {
private final MembersInjector<AppCompatActivity> supertypeInjector;
private final Provider<Coffee> mCoffeeProvider;
public CoffeeShopActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Coffee> mCoffeeProvider) {
assert supertypeInjector != null;
this.supertypeInjector = supertypeInjector;
assert mCoffeeProvider != null;
this.mCoffeeProvider = mCoffeeProvider;
}
@Override
public void injectMembers(CoffeeShopActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
supertypeInjector.injectMembers(instance);
//1.獲取一個(gè)Coffee的實(shí)例链瓦,復(fù)制給CoffeeShopActivity的實(shí)例instance拆魏,從這里可以看出mCoffee不能是私有的。
instance.mCoffee = mCoffeeProvider.get();
}
public static MembersInjector<CoffeeShopActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Coffee> mCoffeeProvider) {
return new CoffeeShopActivity_MembersInjector(supertypeInjector, mCoffeeProvider);
}
}
上面代碼注釋處渤刃,就是實(shí)現(xiàn)注入的地方卖子,通過(guò)CoffeeProvider
獲取一個(gè)Coffee
對(duì)象洋闽,然后復(fù)制給CoffeeShopActivity
對(duì)象的mCoffee
成員變量诫舅。
跟蹤下來(lái),終于知道是在什么地方給要依賴的成員變量賦值的了铐伴。其實(shí)我們可以看出当宴,并不是沒(méi)有用到new
,只不過(guò)Dagger2
通過(guò)自動(dòng)生成代碼户矢,幫你new了梯浪,你就省去了瓢娜,這個(gè)new的過(guò)程眠砾,直接可以用了褒颈。這個(gè)呢谷丸,只是一個(gè)簡(jiǎn)單的成員變量的依賴跟蹤刨疼,其他的通過(guò)module來(lái)提供依賴對(duì)象的例子揩慕,其實(shí)跟蹤路徑和這個(gè)大同小異漩绵。 我這里就以小見(jiàn)大的分析這么個(gè)小例子止吐,更復(fù)雜的用法如果有什么疑問(wèn)碍扔,歡迎留言交流不同。
總結(jié)
我的咖啡店開(kāi)好了二拐,咖啡都不用我自己去拿百新,自然有人給我送過(guò)來(lái)饭望,真是方便至極铅辞。干了這杯咖啡斟珊。最后,我做個(gè)簡(jiǎn)單的關(guān)鍵點(diǎn)總結(jié)吧!
- 被依賴的成員變量的修飾符钩乍,不能是私有的怔锌。否則不能實(shí)現(xiàn)依賴注入涝涤。
- 每次編寫(xiě)完代碼之后阔拳,記得
rebuild project
工程糊肠,自動(dòng)生成代碼货裹。 - 在使用成員變量之前弧圆,一定要先注入依賴搔预,即編寫(xiě)一個(gè)類似于
CoffeeShopActivity
的inject
方法经伙。注入之后才能使用帕膜。 - Component的接口里面的方法垮刹,參數(shù)類型一定要是目標(biāo)類荒典,不能是目標(biāo)類的父類。提供依賴的方法或者構(gòu)造函數(shù)遮咖,返回的類型也一定要是 要被依賴的類的類名御吞,不能是父類。
最后呢揍诽,雖然只是簡(jiǎn)單的一個(gè)成員變量的依賴注入的流程分析寝姿,但是其他復(fù)雜的依賴?yán)佣鋵?shí)看懂了這個(gè)根资,再分析其他的應(yīng)該也挺簡(jiǎn)單的,關(guān)鍵是了解了原理。希望大家多多指點(diǎn)鹰椒,哪里寫(xiě)的不對(duì)的,或者文章編寫(xiě)有什么建議的奸汇,都?xì)g迎大家留言告知我擂找。喜歡的在文末,輕輕點(diǎn)個(gè)贊哦柬采。o 謝謝!Have a nice day肩刃!