google四件套之Dagger2。從入門到愛不釋手,之:Dagger2基礎知識及在Java中使用(1)

前言

網上都說Dagger2是比較難上手的,我在看了大量資料和使用時也遇到了不少不懂或者模糊的知識點,並且大部分博客資料都比較古老。忽然有那麼一瞬間,忽然明白了因此然,故總結了4篇文章。話說在java中使用仍是很繁瑣的,不要怕帶你真正上手,並運用到咱們的Android項目中去。java

本次Dagger2講解總共分4篇:
一、Dagger2基礎知識及在Java中使用(1)
二、Dagger2基礎知識及在Java中使用(2)
三、Dagger2進階知識及在Android中使用
四、Dagger2華麗使用在MVP框架中android

首先簡單申明下,Dagger2的好處不是本文的重點。你們能夠自行百度。Dagger2是依賴註解框架,像咱們之間的butterknife也是這樣的框架,想這樣的框架依賴通常都有2行。第二行是以annotationProcessor開頭的。這實際上是apt的工具,並且這樣的依賴註解框架不會影響性能(不是反射機制),在編譯的時候,apt把要用的代碼生成。因此大可放心使用
再舉我理解的例子(你們不要全信,哈哈稍微不恰當):@Component至關於一個注射器(記住是接口);@Module至關於注射液,就是數據源(記住這裏是類或者抽象類),此時要把注射液放入指定哪一個注射器如:@Component( modules = ... );@Inject 至關於標註被注射體。git

以後的講解都是走完簡單的流程,實現功能,而後講大概理解。貼在博客上的代碼,可能會省略部分代碼,便於理解。github上的Demo及註釋,很是詳細,接近完美0-0#
若是是徹底沒了解過,關於Dagger一些標註的具體介紹和理解,推薦這裏有3篇介紹標註的意思和怎麼工做的github

首先添加依賴

implementation 'com.google.dagger:dagger:2.24'
annotationProcessor "com.google.dagger:dagger-compiler:2.24"
複製代碼

一、@Inject & @Component 的簡單使用(不帶@Module)

首先隨便定義個類:Person,無參構造方法用@Inject標註:bash

public class Person {
    @Inject
    public Person() {

    }
}
複製代碼

而後定義咱們的 Component(這裏稍微提一下,若是一個頁面定義多個Component,你build的時候報錯,是否是)
這裏的命名規則最好是以咱們頁面類名+Component,這樣比較清晰。用@Component標註,裏面有個void方法,方法名隨意定,建議用inject最好,固然也是清晰,參數是咱們須要依賴註解的頁面:
@Component
public interface AstudyActivityComponent {
    void injectTo(AstudyActivity astudyActivity);
}
複製代碼

作好上面步驟後,點開studio裏build標籤下的Make Project。讓apt幫咱們生成代碼,通常生成代碼爲Dagger+你定義Component的類名。

以後這個步驟再也不重複,就是你寫完準備代碼的時候必定要讓apt生成代碼,Make Project下

而後在咱們的Activity裏:app

public class AstudyActivity extends BaseActivity {
    @Inject
    Person person;
    
    @Override
    //這裏是我封裝的onCreate,省略部分代碼,只爲理解,以後都請忽略!
    protected void processLogic() {
        //第一種
        DaggerAstudyActivityComponent.create().injectTo(this);
        //第二種
        //DaggerAstudyActivityComponent.builder().build().injectTo(this);
    }
}
複製代碼

在咱們的Activity裏build下咱們的Component,而後註冊在咱們的Activity裏,就可使用經過咱們的@Inject使用咱們的person了。
這裏初始化有2種:
一、DaggerAstudyActivityComponent.create().injectTo(this);
二、DaggerAstudyActivityComponent.builder().build().injectTo(this); 這個使用module傳值必定要使用框架

那麼一個簡單的使用就實現了,這裏忽略了new的過程,從這個過程就知道他解耦的實現了。ide

簡單使用大體步驟( 看懂請略過 ):

  • 第一步:用Inject標註,告訴dagger2能夠實例化這個類,如:Person
  • 第二步:使用註解Component,表示要將依賴注入到AstudyActivity裏
  • 第三步:使用android studio build下的Make Project生成代碼,使他自動生成DaggerComponent生成的類,類名是:Dagge+咱們定義的Component的名字

二、帶@Module的使用

爲何會有module的概念,好比上面的Person的構造方法能夠用@Inject標註,可是引入的第三方庫但是沒有辦法加的,因此這裏使用@Module能夠解決這個問題。
這裏咱們定義個Human,僞裝他是第三方類庫,裏面沒有使用@Inject函數

public class Human {
    public Human() {

    }
}
複製代碼


接下的步驟先定義咱們的數據源Module,也就是先定義初始化的地方,以前Person的構造方法是用@Inject。首先命名規則最好加上Module,用@Module標註。而後裏面定義個方法,用 @Provides標註。返回值爲咱們須要初始化的類,方法名最好是以Provides結尾。其實這裏能夠定義多個方法,後面說工具

@Module
public class BstudyActivityModule {
    @Provides
    Human humanProvides(){
       return new Human();
    }
}
複製代碼


而後是咱們的Component。這裏與以前不一樣的是(modules = BstudyActivityModule.class),這就至關於把注射液放進注射器。這裏能夠有多個Module,後面說

@Component(modules = BstudyActivityModule.class)
public interface BstudyActivityComponent {
    void injectTo(BstudyActivity bstudyActivity);
}
複製代碼

Make Project後,在Activity裏操做與以前如出一轍。

帶Module使用大體步驟( 看懂請略過 )

  • 一、假設Human不可隨意更改,沒有@Inject標註(第三方類庫,不是你項目裏的代碼確定沒有@Inject)用@module標註BstudyActivityModule,用@Provides標註方法的返回值就是咱們須要inject的類型
  • 二、編寫Component接口使用@Component標註這個接口,並使用modules=的方法連接上第一步中編寫的Module類;
  • 三、接下來就和AstudyActivity中的使用方式同樣了

三、經過Module傳參

這個其實不重要,重要的引出4,5的概念。明白這步,後面纔好理解。
首先咱們假設2個類,女人類,和靈魂類:且靈魂類有個錢的屬性。靈魂類又是女人的屬性。靈魂類以下:

public class Soul {
    private int money;
    public Soul() {

    }
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        this.money = money;
    }
}
複製代碼

女人以下:

public class Woman {
    private Soul soul;

    public Soul getSoul() {
        return soul;
    }

    public Woman(Soul soul) {
        this.soul = soul;
    }
}
複製代碼


首先仍是定義咱們的Module先。既然能夠傳參,固然是有個money的屬性。最終咱們依賴註解是要使用Woman類。咱們的providesWoman方法用@Provides標註,這個時候他回去找Soul的初始化,先經過@Provides去找Soul。這個時候找到了providesSoul。這樣就造成了女人類。假如這個時候沒有providesSoul。他會去找Soul類裏有沒有用@Inject標註的構造函數。若是尚未,那麼很差意思。出錯

@Module
public class CstudyModule {
    private int money;
    
    public CstudyModule(int money) {
        this.money = money;
    }

    @Provides
    Soul providesSoul() {
        Soul soul = new Soul();
        soul.setMoney(this.money);
        return soul;
    }

    @Provides
    Woman providesWoman(Soul soul) {
        return new Woman(soul);
    }
}
複製代碼

接下來是Component,沒有變化

@Component(modules = CstudyModule.class)
public interface CstudyActivityComponent {
    void injectTo(CstudyActivity cstudyActivity);
}
複製代碼

Activity有些許變化,固然是傳參了。咱們給女人的靈魂傳了100塊,對,女人只值100塊!

public class CstudyActivity extends BaseActivity {
    @Inject
    Woman woman;
    @Override
    protected void processLogic() {
        DaggerCstudyActivityComponent.builder()
            .cstudyModule(new CstudyModule(100))
            .build().injectTo(this);
    }
}
複製代碼

注意點( 看懂請略過 ):

  • 在Module的構造函數帶有參數且參數被使用的狀況下,所生產的Component類就沒有create()方法了。
  • 在這裏的module若是沒有providesSoul()方法的話,還有一種狀況只要在Soul的構造方法有@Inject也是可行的。

四、使用@Component.Builder(需先了解 三、經過Module傳參)

咱們把三、經過Module傳參apt生成的代碼點開DaggerCstudyActivityComponent;看下圖是否是發現了一個Builder類,這是apt幫咱們自動生成的,咱們固然也能本身實現

仍是以 三、經過Module傳參的例子,咱們不用系統幫咱們生成的Builder,本身定義。前面的步驟都同樣,直接來看咱們的Component。
本身定義個接口類Builder,並用@Component.Builder標註裏面有2個方法:

  • 方法一:是返回值Builder的方法,這裏若是傳module就會以咱們傳的爲主,不然他會幫咱們生成一個money爲0的module。固然你也隨意傳數據類型,只不過無效。能夠試試,
  • 方法二:是返回值爲當前Component的方法,方法名其實均可以自定義,當最好以規範爲主,用習慣了就明白了
@Component(modules = CstudyModule.class)
public interface DstudyActivityComponent {
    void injectTo(DstudyActivity dstudyActivity);
    
    @Component.Builder
    interface Builder {
        Builder cstudyModule(CstudyModule cstudyModule);
        DstudyActivityComponent build();
    }
}
複製代碼


Activity裏使用是同樣的。只不過咱們把系統自動幫咱們生成的,本身去寫了而已。仍是貼下Activity代碼吧

public class DstudyActivity extends BaseActivity {
    @Inject
    Woman dWoman;
    @Override
    protected void processLogic() {
        DaggerDstudyActivityComponent.builder()
            .cstudyModule(new CstudyModule(100))
            .build().injectTo(this);
    }
}
複製代碼

大體理解和總結爲( 看懂請略過 ):

  • 經過咱們cstudy的內容,你能夠點開cstudyModule查看源碼,能夠看到有個Builder cstudyModule(CstudyModule cstudyModule){}。這是dagger2自動生成的(你還能夠經過,app/build/generated/source/apt/debug/你的包名/DaggerAppComponent.java 目錄下找到)


因此@Component.Builder的用法,用module傳參的例子。其餘都不用變,要變的是Component,定義個Builder並用@Component.Builder標註。這裏有2個方法:

  • 方法一:是返回值Builder的方法,這裏若是傳module就會以咱們傳的爲主,不然他會幫咱們生成一個money爲0的module。固然你也隨意傳數據類型,只不過無效。能夠試試
  • 方法二:是返回值爲當前Component的方法,方法名其實均可以自定義,當最好以規範爲主,用習慣了就明白了

五、使用@BindsInstance(需先了解 四、使用@Component.Builder)

這個時候你又說了,傳參,咱們老是要new CstudyModule(100)。原本說Dagger2在使用的時候省略new的過程,解耦。但這裏還要new,很low是否是。不急不急,強大的google把一切都想好了。這個時候遇到一個新的標註@BindsInstance。
@BindsInstance 大體這裏能夠理解爲幫咱們省去寫類的構造方法,而直接去賦值

省掉構造方法,那麼固然是首先改咱們的Module。咱們去掉Module的構造方法及money成員變量屬性,把money加到providesSoul裏成型參。看到這裏,這裏又可理解爲@BindsInstance 其實去找@Provides標記的方法的參數,假如類型一致就去初始化

@Module
public class EstudyModule {
    @Provides
    Soul providesSoul(int money) {
        Soul soul = new Soul();
        soul.setMoney(money);
        return soul;
    }

    @Provides
    Woman providesWoman(Soul soul) {
        return new Woman(soul);
    }
}
複製代碼


而後是修改的Component,改完Module,固然是modules = EstudyModule.class。這些我就忽略了,看了上面的步驟你也明白,我就直接說關鍵地方了。用@BindsInstance標註咱們返回值爲Builder的方法。裏面的參數改爲咱們的int Money。固然改爲咱們用@Provides標註的類型其實均可以,只不過這裏你若是改爲Soul soul,固然你初始化仍是要傳new Soul。過程就是這個過程

@Component(modules = EstudyModule.class)
public interface EstudyActivityComponent {
    void injectTo(EstudyActivity estudyActivity);
    
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder initMoney(int money);
        EstudyActivityComponent build();
    }
}
複製代碼


最後是咱們的Activity

public class EstudyActivity extends BaseActivity {
    @Inject
    Woman woman;
    @Override
    protected void processLogic() {
        DaggerEstudyActivityComponent.builder()
            .initMoney(100)
            .build().injectTo(this);
    }
}
複製代碼

看到這裏,是否是以爲Dagger2還比較有意思。更有意思的在後面。固然也愈來愈繞了,可是你得興奮起來,精髓啊。

六、Component依賴Component,使用dependence

這裏咱們以Activity和Fragment爲例。假設咱們再Activity依賴注入Human類,此時在Fragment裏使用
先看定義Module,和以前同樣,沒什麼區別

@Module
public class FstudyActivityModule {
    @Provides
    Human providesHuman() {
        return new Human();
    }
}
複製代碼


再建ActivityComponent固然這裏,你也能夠加上void inject(FstudyActivity fstudyActivity)。重要一點是咱們要把依賴注入的類返回出去,定義方法provideHuman,由於是Component依賴Component。因此也能理解

@Component(modules = FstudyActivityModule.class)
public interface FstudyActivityComponent {
    Human provideHuman();
}
複製代碼


再使用dependencies建FragmentComponent暫且能夠理解爲子Component,由於後面真的有子Component。dependencies = FstudyActivityComponent.class寫上咱們的父Component。後面是注入到Fragment裏

@Component(dependencies = FstudyActivityComponent.class)
public interface TestFragmentComponent {
    void inject(TestFragment testFragment);
}
複製代碼


在Activity裏,要先生成ActivityComponent,而後提供個方法,把父Component提供給Fragment

public class FstudyActivity extends BaseActivity {
    private FstudyActivityComponent fstudyActivityComponent;
    @Override
    protected void processLogic() {
        fstudyActivityComponent = DaggerFstudyActivityComponent.create();
    }

    public FstudyActivityComponent getFstudyActivityComponent() {
        return fstudyActivityComponent;
    }
}
複製代碼


在Fragment裏

public class TestFragment extends BaseFragment {
    @Inject
    Human human;
    @Override
    protected void processLogic(Bundle savedInstanceState) {
        FstudyActivityComponent fstudyActivityComponent = ((FstudyActivity) getActivity()).getFstudyActivityComponent();
        DaggerTestFragmentComponent.builder()
                .fstudyActivityComponent(fstudyActivityComponent)
                .build().inject(this);
    }

}
複製代碼

好了,在fragment可使用human了。在java裏使用,確實很繞,代碼多的讓你難以接受。建議先理解,後面出的一篇在Android中使用,你會很爽。

大體理解和總結爲( 看懂請略過 ):

  • 一、假設咱們用Human注入,這裏的FstudyActivityModule 和以前的Module同樣,正常
  • 二、FstudyActivityComponent把要注入的類返回
  • 三、在TestFragment方面,咱們新建一個TestFragmentComponent 依賴 FstudyActivityComponent;
  • 四、在FstudyActivity自定義一個方法把FstudyActivityComponent提供出去,供TestFragment使用
  • 五、在TestFragment,註冊下就OK了。很繞,我的建議先明白這個流程就行了

七、Component依賴Component,使用@subComponent(這個和 【標題6】 實現的是同一個效果)

雖然是實現同一個效果,可是方式不一樣,目的是讓你更多瞭解Dagger2。一樣以上面的例子。Module和上面同樣不變(我這裏是爲了Demo區域化,雖然類名不一樣,可是內容是一致的)
首先建子Component,FragmenComponent,用@Subcomponent標註,並注入咱們的Fragment裏。爲何先建子Component呢。由於子Component要在父Component返回,繞不繞!!

@Subcomponent
public interface DemoFragmentComponent {
    void inject(DemoFragment demoFragment);
}
複製代碼


而後是父Component,ActivityComponent,父Component一切正常,返回值是子Component

@Component(modules = GstudyActivityModule.class)
public interface GstudyActivityComponent {
    DemoFragmentComponent demoFragmentComponent();
}
複製代碼


在Activity裏的操做同樣,初始化咱們的父Component,並提供方法,返回父Component,供Fragment使用。
而後是Fragment裏

public class DemoFragment extends BaseFragment {
    @Inject
    Human human;
    @Override
    protected void processLogic(Bundle savedInstanceState) {
        GstudyActivityComponent gstudyActivityComponent = ((GstudyActivity) getActivity()).getGstudyActivityComponent();
        gstudyActivityComponent
            .demoFragmentComponent()
            .inject(this);
    }

}
複製代碼

這樣就成功了,能夠在Fragment使用human了。看明白了標題6,其實標題7原理是同樣的。

大體理解和總結爲( 看懂請略過 ):

  • 一、先建一個子類Component,用@subComponent標註,DemoFragmentComponent
  • 二、而後建父類Component: GstudyActivityComponent,定義個方法,返回子類Component。
  • 三、在GstudyActivity自定義一個方法把GstudyActivityComponent提供出去,供DemoFragment使用
  • 四、在DemoFragment,註冊下,就行了。大體和dependencies相似
  • 注意:但註冊的時候寫法不一樣,以前是經過子Component傳入父Component;而這裏是從父Component中獲取子Component,而後直接inject

八、Component依賴Component,使用 @Subcomponent.Builder(和【標題6】&【標題7】實現的是同樣的效果)

效果同樣,方式不一樣,目的仍是更瞭解Dagger2。能夠看到這裏的標註是@Subcomponent.Builder。因此和使用@Subcomponent相似。


好了,仍是以上面的例子爲例。這裏須要改的是父Component和子Componet。這個時候咱們難免想到@Component.Builder的用法。是否是同樣呢。這個時候我只能說相似,可是又不同。畢竟這裏多了個sub。咱們先看下@Component.Builder的用法,拷貝以前代碼(不知道再去回顧下【標題4】)

@Component(modules = CstudyModule.class。)
public interface DstudyActivityComponent {
    void injectTo(DstudyActivity dstudyActivity);

    @Component.Builder
    interface Builder {
        Builder cstudyModule(CstudyModule cstudyModule);
        DstudyActivityComponent build();
    }
}
複製代碼


咱們按照找個方式去寫@Subcomponent.Builder。,@Subcomponent.Builder要使用確定是在@Subcomponent下,毋庸置疑。首先發現沒有modules = CstudyModule.class。被@Subcomponent取代了。沒有Module咱們就使用無參

@Subcomponent
public interface OtherFragmentComponent {
    void inject(OtherFragment otherFragment);
    @Subcomponent.Builder
    interface Builder {
        Builder noModule();    
        OtherFragmentComponent build();
    }
}
複製代碼

我先告訴告訴你運行結果把,運行結果報錯了。

@Subcomponent.Builder types must have exactly one zero-arg method, and that method must return the @Subcomponent type. Already found: hstudyActivityModule()

報錯信息以下:意思是不須要Build返回值方法,經過Already found: hstudyActivityModule()知道,已經發現了咱們的Module。咱們再想一想這個標註的名稱sub,不就是子Component繼承父Componet嗎。並且Dagger2內部已經默認了,因此這裏沒有Builder返回值方法。因此正確的子Component

@Subcomponent
public interface OtherFragmentComponent {
    void inject(OtherFragment otherFragment);
    @Subcomponent.Builder
    interface Builder {
        OtherFragmentComponent build();
    }
}
複製代碼


接下來是父Component,返回值固然是咱們的Builder。

@Component(modules = HstudyActivityModule.class)
public interface HstudyActivityComponent {
    OtherFragmentComponent.Builder sonbuilder();
}
複製代碼

有人就疑惑了不能夠返回子Component嗎。咱們假如此時返回子Component,我先告訴你運行報錯,信息以下:

Components may not have factory methods for subcomponents that define a builder.

大概意思是:用了@Subcomponent.Builder的話,Component沒有工廠模式方法去建立咱們的子Component。好了,就這樣,請原諒個人英語四級!!


Activity仍是和以前同樣,初始化咱們的父Component,並經過方法返回。Fragment裏使用依賴以下

public class OtherFragment extends BaseFragment {
    @Inject
    Human human;
    @Override
    protected void processLogic(Bundle savedInstanceState) {
        HstudyActivityComponent hstudyActivityComponent = ((HstudyActivity) getActivity()).getHstudyActivityComponent();
        hstudyActivityComponent.
                sonbuilder().build().inject(this);
    }

}
複製代碼

好了,繞來繞去,功能實現了!看到這裏對Dagger2大體瞭解了吧。

因爲在java裏使用比較多。臨時決定java分2篇。否則博客太長,也沒人看。

本文github Demo地址

花了老大勁,別誤會,不是要錢。能不能留下個足跡star啊

相關文章
相關標籤/搜索