Dagger2.1不是Dagger2

一、前言

在學(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

https://google.github.io/dagger/android

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容