前言
本篇文章將結(jié)合我自己寫(xiě)的咖啡店賣(mài)咖啡的栗子的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)建一家銷(xiāo)售咖啡的咖啡店,然后在里面買(mǎi)各種咖啡,黑咖啡呀等。既然是咖啡店,那肯定要有咖啡呀。所以咖啡就是我需要的一個(gè)對(duì)象。所以我的咖啡店還需要依賴于咖啡,沒(méi)咖啡,那怎么行,準(zhǔn)備好票子,來(lái)買(mǎi)我的咖啡吧。根據(jù)這個(gè)需求,我的咖啡店類(lèi)如下:
public class CoffeeShopActivity extends AppCompatActivity {
// 1. 我要賣(mài)的咖啡對(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)單的類(lèi),里面提供了一個(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依賴于類(lèi)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注釋?zhuān)缓驝offee的構(gòu)造函數(shù)添加了@Inject注釋。這樣就CoffeeShopActivity告訴了Dagger2,我需要Coffee的實(shí)例,然后Dagger2就去找到Coffee的構(gòu)造函數(shù)創(chuàng)建一個(gè)Coffee。難道就只要這兩個(gè)類(lèi)就可以了嗎,之前不是說(shuō)Component是目標(biāo)類(lèi)與被注入類(lèi)之間的橋梁?jiǎn)幔?Component呢? 沒(méi)錯(cuò),我還沒(méi)貼出Componet呢。自定義個(gè)Componet類(lèi)如下:
@Component
public interface CoffeeShopComponent {
void inject(CoffeeShopActivity coffeeShopActivity);
}
這個(gè)Component是不是很簡(jiǎn)單,只有一個(gè)抽象函數(shù),方法名是inject,方法名不是必須是inject,也可以是其他名字,但是參數(shù)一定要是你的目標(biāo)類(lèi),而且注意哦,不能是Activity,一定要是目標(biāo)類(lèi)自己的類(lèi)名。在第一篇文章里面就有提到過(guò),這里再?gòu)?qiáng)調(diào)一下,Dagger框架是通過(guò)參數(shù)的類(lèi)型以及返回值類(lèi)型,找到要依賴的類(lèi),從而建立聯(lián)系的,所以類(lèi)名一定要是直接要注入依賴的目標(biāo)類(lèi),返回值也是要注入的類(lèi)。
好了,注射器(Component)寫(xiě)好了,那要派上用場(chǎng)呀!那是在哪里用注射器把依賴注入了MainActivity呢。請(qǐng)看,我在MainActivity里面第2個(gè)注釋?zhuān)琲nject()方法。沒(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的類(lèi)。文件路徑是moudle路徑\build\generated\source\apt\debug\com\gotech\changelauncherbg\demodagger\DaggerCoffeeShopComponent,下面自動(dòng)生成的類(lèi)都在這個(gè)路徑里面。當(dāng)然APT還根據(jù)注釋自動(dòng)生成了其他的代碼。我們先分析這個(gè),因?yàn)檫@個(gè)是實(shí)現(xiàn)依賴注入的入口類(lèi)。注意啦,自動(dòng)生成代碼,是要你執(zhí)行了rebuild project才會(huì)自動(dòng)生成的,所以呢你編寫(xiě)完代碼之后記得一定要rebuild才能得到這些類(lèi)。
好吧,下面就一起來(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)部建造類(lèi)Builder,可以看成一個(gè)簡(jiǎn)單的建造模式的應(yīng)用。利用Builder來(lái)建造一個(gè)DaggerCoffeeShopComponet對(duì)象然后利用CoffeeShopActivity_MembersInjector初始化coffeeShopActivityMembersInjector。這個(gè)是真正實(shí)現(xiàn)依賴注入的類(lèi)。從類(lèi)名MembersInjector<CoffeeShopActivity> 可以看出這是一個(gè)用于注入成員變量的注入器,然后要注入的目標(biāo)類(lèi)是CoffeeShopActivity。 接下來(lái),我們看看CoffeeShopActivity_MembersInjector這個(gè)類(lèi)的代碼。
@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è)類(lèi)似于
CoffeeShopActivity的inject方法。注入之后才能使用。 - Component的接口里面的方法,參數(shù)類(lèi)型一定要是目標(biāo)類(lèi),不能是目標(biāo)類(lèi)的父類(lèi)。提供依賴的方法或者構(gòu)造函數(shù),返回的類(lèi)型也一定要是 要被依賴的類(lèi)的類(lèi)名,不能是父類(lèi)。
最后呢,雖然只是簡(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!