一、前言
在學(xué)習(xí)android architecture components(簡(jiǎn)稱acc)時(shí)下載了google官方demo,demo里有一部分是關(guān)于dagger+mvvm(mvp)+acc的,本以為閱讀起來(lái)沒有壓力但結(jié)果卻是一臉懵逼,是的,dagger的寫法以及注解完全陌生。難道dagger更新了?本著落后就要挨打的原則去google了一番,發(fā)現(xiàn)國(guó)內(nèi)資料通篇都是dagger原本的用法,并沒有找到我需要的。最后終于在國(guó)外網(wǎng)站找到了一篇dagger2.1新用法的介紹,配合dagger官網(wǎng)終于了解了一二,這也堅(jiān)定了我從頭寫一篇Dagger文章的決心。
本文適合人群:1、從未了解過dagger,2、僅簡(jiǎn)單使用API但不了解為何這樣使用,3、可以很好的使用并理解dagger,僅需了解新版本(可以直接跳到第四部分)
二、 Dependency Injection(DI)
翻譯成中文也就是依賴注入,我們先用一個(gè)很簡(jiǎn)單的例子來(lái)了解一下
class Clothes {
final int Defense = 5;
}
class Pants {
final int Defense = 10;
}
class hero {
void printDefense() {
Clothes clothes = new Clothes();
Pants pants = new Pants();
System.out.print("您的角色擁有防御值: "+clothes.Defense + pants.Defense);
}
}
這是一段很簡(jiǎn)單的代碼,初始化角色時(shí),擁有衣服褲子兩件裝備。但仔細(xì)查看,在hero類里創(chuàng)建了clothes和pants兩個(gè)類,造成了耦合,當(dāng)你修改clothes或pants時(shí)很可能影響到hero類。所以我們通常會(huì)將上述代碼優(yōu)化如下:
class hero {
private Clothes clothes;
private Pants pants;
public hero(Clothes clothes, Pants pants) {
this.clothes = clothes;
this.pants = pants;
}
void printDefense() {
System.out.print("您的角色擁有防御值: "+clothes.Defense + pants.Defense);
}
}

沒錯(cuò)放到構(gòu)造方法里,這就是'依賴注入'了,hero依賴了clothes和pants,同樣的,set方法也可以進(jìn)行依賴注入。大家是不是在不知不覺中也使用過類似的方式呢:)
對(duì)比一下兩種方式,前者的缺點(diǎn)如下:
1、clothes和pants無(wú)法重用,降低了代碼的重用性
2、增加了代碼的耦合性,一旦clothes和pants更改可能需要修改hero類很多地方
3、很難進(jìn)行單元測(cè)試
好的,接下來(lái)我們就開始用依賴注入的方法創(chuàng)建hero
public static void main(String[] args){
Clothes clothes = new Clothes();
Pants pants = new Pants();
Hero hero = new Hero(clothes, pants);
hero.printDefense();
}
這樣子看起來(lái)沒什么大問題,但是當(dāng)依賴多了,或者依賴也依賴了其他依賴,就會(huì)相當(dāng)臃腫。有點(diǎn)繞嗎?沒關(guān)系我們看代碼:
public static void main(String[] args) {
Color red = new Color();
Clothes clothes = new Clothes(red);
Pants pants = new Pants();
Shoes shoes = new Shoes();
Hat hat = new Hat();
....
Hero hero = new Hero(clothes, pants, shoes, hat, ....);
hero.printDefense();
}
上面的代碼中Clothes又依賴了Color,說(shuō)不定Color又可能會(huì)依賴些什么,加上下面的Pants、Shoes、hat......等等,構(gòu)造我們的Hero需要太多依賴,這樣我們使用hero時(shí)就非常不方便,主方法就會(huì)越來(lái)越難以維護(hù)。
三、Dagger基本使用
這樣,Dagger就孕育而生了,他是一個(gè)DI框架,會(huì)讓我們的依賴注入工作顯得非常輕松。自動(dòng)生成依賴,而我們只需要添加注解即可。
我們用Dagger的方式來(lái)重寫上面的代碼:
class Clothes {
final int Defense = 5;
@Inject
public Clothes() {
}
}
class Pants {
final int Defense = 10;
@Inject
public Pants() {
}
}
class Hero {
private static final String TAG = "Hero";
private Clothes clothes;
private Pants pants;
@Inject
public Hero(Clothes clothes, Pants pants) {
this.clothes = clothes;
this.pants = pants;
}
public void printDefense() {
Log.e(TAG, "您的角色擁有防御值: " + (clothes.Defense + pants.Defense));
}
}
是的,就是這么簡(jiǎn)單,只需要添加@Inject注解,Dagger就能幫我們自動(dòng)構(gòu)建Hero對(duì)象,當(dāng)我們使用時(shí)就像這樣:
Component
interface HeroComponent {
Hero getHero();
}
public static void main(String[] args){
// Clothes clothes = new Clothes();
// Pants pants = new Pants();
// Hero hero = new Hero(clothes, pants);
// hero.printDefense();
HeroComponent component = DaggerHeroComponent.create();
Hero hero = component.getHero();
hero.printDefense();
}
我們來(lái)梳理一下:
1、首先創(chuàng)建一個(gè)component接口并添加@component注解,添加獲取Hero的方法
2、給Hero構(gòu)造方法添加@Inject注解,表示需要自動(dòng)生成該類
3、給依賴類添加@Inject,表示改類也需要自動(dòng)生成,如此遞歸添加直到所有依賴都能自動(dòng)生成
4、構(gòu)建項(xiàng)目(必須先構(gòu)建,Dagger會(huì)幫你生成一些輔助類),使用名為‘Dagger+YourComponent’的類創(chuàng)建Hero對(duì)象
5、愉快的使用吧!
這樣一來(lái)無(wú)論Hero需要多少依賴,我們都可以很簡(jiǎn)單的生成,不會(huì)造成類的臃腫和耦合,使用Hero時(shí)不用關(guān)心依賴從哪里來(lái),怎么來(lái)的,就算Hero或依賴構(gòu)建方式改變,也不需要修改調(diào)用的main函數(shù),很好的解耦。
這時(shí)有些童鞋就會(huì)問了,如果我需要使用類似Retrofit、OkHttp之類的三方庫(kù),而我們卻無(wú)法去庫(kù)里添加@Inject注解,該怎么辦?沒關(guān)系,Dagger同樣為大家準(zhǔn)備了對(duì)策:@Module、@Provides
@Module:類似一個(gè)倉(cāng)庫(kù),提供Retrofit之流的實(shí)例
@Provides:倉(cāng)庫(kù)里具體產(chǎn)品的標(biāo)志,表示該產(chǎn)品對(duì)外提供
@Module
public class MyModule {
@Provides
public Retrofit ProvideRetrofit() {
return new Retrofit.Builder()
.baseUrl("www.google.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(setupClient())
.build();
}
}
這樣就寫好了一個(gè)提供Retrofit的方法,我們還需要將module提交到component:
Component(modules = MyModule.class)
interface HeroComponent {
Hero getHero();
Retrofit getRetrofit();
}
這樣,我們就可以愉快的使用Retrofit了,使用方式和Hero一樣,我就不累述了。
關(guān)于其他API大家可以查閱資料,本文重在思路介紹。
建議初學(xué)者在閱讀以下內(nèi)容前先了解更多Dagger2的使用,推薦文章:給初學(xué)者的Dagger2
因?yàn)橐韵聝?nèi)容僅適合使用過Dagger2的童鞋了?。?!
四、Dagger&Android
打起精神,重點(diǎn)來(lái)了?。?/strong>
在講解重點(diǎn)之前,我們先來(lái)看一段代碼:
((MyApplication) getApplication())
.getAppComponent()
.myActivity(new MyActivityModule(userId))
.build()
.inject(this);
在熟悉使用過Dagger2的coder眼中,這段代碼幾乎貫穿了整個(gè)應(yīng)用程序,每一個(gè)activity都會(huì)調(diào)用同樣的代碼,相信大家也想盡辦法去優(yōu)化該段代碼,包括將其放入Base中或者做了各種各樣的封裝,可能都不太盡人意(至少筆者沒有找到最優(yōu)解-_-)。
沒錯(cuò)!官方這次升級(jí)完美的解決了該問題,以前的實(shí)現(xiàn)方式我就不多說(shuō)了,直接講述新版本方案。
我先按流程走一遍,讓大家熟悉熟悉:
1、老規(guī)矩,gradle添加依賴
implementation 'com.google.dagger:dagger-android:2.15'
implementation 'com.google.dagger:dagger-android-support:2.15' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.15'
annotationProcessor 'com.google.dagger:dagger-compiler:2.15'
筆者寫的時(shí)候最新版為2.15,大家使用時(shí)可以更換為官方最新版。
2、創(chuàng)建AppComponent
@Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
ActivityBuilder.class})
public interface AppComponent extends AndroidInjector<MyApplication> {
@Override
void inject(MyApplication app);
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}
@Component.Builder:自定義component構(gòu)造器.如果看過Dagger2源碼的童鞋應(yīng)該知道component是用Builder模式創(chuàng)建的,而這里提供了一個(gè)自定義構(gòu)造器。目前先不管,你甚至可以刪除這個(gè)Builder都沒有影響,Dagger會(huì)自動(dòng)生成,這里我只是想展示一下這個(gè)注解
繼承了AndroidInjector類,重寫了他的Inject方法,這個(gè)方法必須寫,初始化AppComponent用。
@Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
MainActivityModule.class})
Component注解里添加了需要使用到的Module
AndroidSupportInjectionModule.class:這是Dagger2.1初始化必須的module,由Dagger內(nèi)部完成,用于提供各種AndroidInjector,我們只需要固定加上即可
AppModule.class:用于Application需要初始化提供的全局module,比如剛剛提到的Retrofit、Okhttp等全局對(duì)象
ActivityBuilder.class:這就是我們Activity所需的Module了
前兩者我們先不關(guān)注,重點(diǎn)看看ActivityBuilder,這和我們以前的使用方式就不大相同了,先來(lái)看看他的實(shí)現(xiàn)。
3、創(chuàng)建ActivityBuilder
@Module
public abstract class ActivityBuilder {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMainActivity(MainActivityComponent.Builder builder);
}
這里是所有Activity(Fragment等)的Component.builder的集合,需要添加@Binds @IntoMap @ActivityKey注解,暫且不管他們的具體作用,只需要了解@Binds就類似于@Provides就可以了。這個(gè)類的作用就是讓Dagger知道我們所有的使用地。
4、創(chuàng)建MainActivityComponent
@Subcomponent(modules = {MainActivityModule.class})
public interface MainActivityComponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<MainActivity> {
}
}
需要使用@Subcomponent注解,所以不需要再像以前寫Inject之類的方法,modules和以前一樣
值得一說(shuō)的就是內(nèi)部類Builder,是固定寫法,和第三步配套使用。
5、創(chuàng)建MainActivityModule
@Module
public class MainActivityModule {
@Provides
MainView provideMainView(MainActivity mainActivity){
return mainActivity;
}
@Provides
MainPresenter provideMainPresenter(MainView mainView, ApiService apiService) {
return new MainPresenter(mainView, apiService);
}
}
這個(gè)也和以前一樣,提供需要的依賴
不要忘記在Appmodule里添加subComponent注釋,如果Activity多了這里就會(huì)添加非常多的XXComponent
@Module(subcomponents = {MainActivityComponent.class,XX,XX,XX.....})
public abstract class AppModule {
}
6、初始化Application
public class MyApplication extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().application(this).build().inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
繼承HasActivityInjector,重寫activityInjector方法,返回一個(gè)DispatchingAndroidInjector
然后在oncreate里初始化
7、MainActivity使用
public class MainActivity extends AppCompatActivity implements MainView {
@Inject
MainPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter.loadMain();
}
@Override
public void onMainLoaded() {
((TextView)findViewById(R.id.hw)).setText("hello Dagger2.1");
}
}
調(diào)用AndroidInjection.inject(this)即可,無(wú)須再調(diào)用一長(zhǎng)串Dagger鏈。
到這里,初步使用就已經(jīng)結(jié)束了,看到這里可能會(huì)覺得‘尼瑪,好像更麻煩了’,‘繞來(lái)繞去頭都大了’的感慨,沒錯(cuò),確實(shí)是更麻煩了,不過別著急,前面我只是希望大家看看最基礎(chǔ)的用法,然后再介紹更簡(jiǎn)潔的方式,不然大家很可能只會(huì)用而不知道其中的來(lái)歷。
優(yōu)化一:繼承DaggerApplication、DaggerActivity等類
大家應(yīng)該還記得剛剛提到的Application類的實(shí)現(xiàn),需要:
1、繼承HasActivityInjector,2、重寫activityInjector方法,3、返回一個(gè)DispatchingAndroidInjector
其實(shí)不僅是Application,在Activity、Fragment里只要有subcomponent就必須要實(shí)現(xiàn)類似的類,舉個(gè)栗子:
一個(gè)Activity里有一個(gè)Fragment,fragment需要依賴注入,這時(shí)候Activity就必須
1、繼承HasFragmentInjector, HasSupportFragmentInjector
2、重寫supportFragmentInjector、fragmentInjector方法
3、返回DispatchingAndroidInjector<Fragment>、DispatchingAndroidInjector<android.app.Fragment>
這樣的話將會(huì)非常麻煩而且重復(fù),但只需要繼承了DaggerApplication、DaggerActivity,一切都變得簡(jiǎn)單了,Dagger2.1都幫你搞定了。這里放出優(yōu)化后的Application:
public class MyApplication extends DaggerApplication {
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
AppComponent appComponent = DaggerAppComponent.builder().application(this).build();
appComponent.inject(this);
return appComponent;
}
}
對(duì)比上面的是不是簡(jiǎn)單了很多,其實(shí)Activity會(huì)更明顯,而且每個(gè)Activity節(jié)省的代碼加起來(lái)是不可計(jì)數(shù)的,就留給大家自己去嘗試了。
優(yōu)化二:使用注解@ContributesAndroidInjector
這是Dagger2.1最強(qiáng)大的注釋,會(huì)幫助你節(jié)省數(shù)以萬(wàn)計(jì)的代碼,可能你看到之前的介紹還并不希望升級(jí)/使用Dagger2.1,但是用了他之后你就會(huì)愛上他。
還記得我們的ActivityBuilder嗎?多了@Binds @IntoMap @ActivityKey注解,可能之前我們根本沒有用到過,并用他們關(guān)聯(lián)到Component.Builder,同樣是重復(fù)的工作。利用@ContributesAndroidInjector可以省略這一切,于是我們的ActivityBuilder就變成了這樣:
@Module
public abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity bindMainActivity();
@ContributesAndroidInjector(modules = {SecondActivityModule.class, SecondFragmentProvider.class})
abstract SecondActivity bindSecondActivity();
}
而我們的MainActivityComponent類就可以刪掉了,是的沒錯(cuò),直接刪掉吧!Dagger會(huì)幫我們自動(dòng)生成,這樣每個(gè)Activity只需要對(duì)應(yīng)一個(gè)Module即可,還記得以前每個(gè)Activity對(duì)應(yīng)一個(gè)Component和一個(gè)Module嗎?
AppModule也可以不需要列出所有Module了,直接刪掉!
五、結(jié)語(yǔ)
通過優(yōu)化后,你的代碼已經(jīng)非常簡(jiǎn)潔了,大家可以嘗試用新版的Dagger來(lái)寫,然后對(duì)比以前的方式,你一定會(huì)迫不及待使用新版的。
本文完整代碼已經(jīng)提交到GitHub
分為兩個(gè)分支,master中使用了初始版,complete中使用了簡(jiǎn)潔的方式,并利用Dagger實(shí)現(xiàn)了MVP模式以及綁定Fragment的方式,大家一定要下載閱讀,這樣更有利于理解。
當(dāng)然如果覺得有用的話記得點(diǎn)一下star,這是對(duì)作者的最大鼓勵(lì)。
地址:https://github.com/mrqatom/DaggerInjection
下期預(yù)告:@Binds詳細(xì)講解、Dagger2.1源碼講解
參考文檔:
https://medium.com/@iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe
https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-dagger-2-part-i-f2de5564ab25