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

前言

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

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

經過第一篇文章的學習,已經對Dagger2大體瞭解了。接下來這篇在Java裏的使用。主要講一些其餘標註。注重的是細節github

仍是老樣子,博客貼的代碼,有部分省略,只爲便於理解。
app

一、@Named的用法

它的用法有點像Tag,提早透露下,@Named是@Qualifier的一種。這裏咱們舉個例:假如咱們有個生成狗的機器。經過傳入不一樣type生成不一樣的狗狗框架


狗狗的類ide

public class Dog {
    private String Type;
    
    public Dog(String type) {
        this.Type = type;
    }

    public String getType() {
        return Type;
    }

    public void setType(String type) {
        Type = type;
    }
}
複製代碼


接下來是Module,意思用@Named標識2種不一樣的初始化路徑。假如這個時候不使用@Named的話,你運行的會報錯,報錯。Dagger2不知道,到底使用哪一個函數

@Module
public class DogModule {
    @Named("tag_1")
    @Provides
    Dog providesJinDog(){
        return new Dog("金毛");
    }

    @Named("tag_2")
    @Provides
    Dog providesEhaDog(){
        return new Dog("二哈");
    }
}
複製代碼


Component仍是正常的,仍是貼下代碼吧post

@Component(modules = DogModule.class)
public interface DogComponent {
    void inject(IstudyActivity istudyActivity);
}
複製代碼


Activity裏的使用學習

public class IstudyActivity extends BaseActivity {
    @Inject
    @Named("tag_1")
    Dog dog1;
    @Inject
    @Named("tag_2")
    Dog dog2;
    
    @Override
    protected void processLogic() {
        DaggerDogComponent.create().inject(this);
        LogUtils.i("看看狗的種類",dog1.getType());
        LogUtils.i("看看狗的種類",dog2.getType());
    }
}
複製代碼


看看運行的結果,知道@Named的用法了吧ui


二、@Qualifier的用法

@Qualifier是限定符,它的做用很像函數的重載。以前說了@Named是@Qualifier的一種。看下@Named的源碼:

是否是清晰了。 這裏咱們自定義一個本身的Named

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface MyQualifier {
    String value() default "";
}
複製代碼

這樣就行了。接下來使用就和@Named如出一轍。直接使用咱們的標註@MyQualifier就好了。
固然你也能夠不要String value() default "";方法。那麼久沒有Tag了。只能標識一個


接下來講個比較特殊的,直接貼Module代碼。他能夠初始化數據。沒錯!牛逼!呸呸呸,是強悍。

@Module
public class DogOtherModule {
 
    @MyQualifier("tag_1")
    @Provides
    String providesChina(){
        return "中華田園犬";
    }

    @MyQualifier("tag_2")
    @Provides
    Dog providesfun(@MyQualifier("tag_1")String dogType){
        return new Dog(dogType);
    }
}
複製代碼

三、@Singleton & @Scope 的用法

首先咱們看看@Singleton的源碼,這麼明顯,@Singleton是@Scope的一種

@Scope  //註明是Scope
@Documented   //標記文檔提示
@Retention(RUNTIME)  //運行時候級別
public @interface Singleton {}
複製代碼


在接下來的講解,我想先說下概念:@Singleton 是做用域單例,是做用域單例,是做用域單例(也能夠理解爲生命週期。Component在哪build就跟那個類的生命週期綁定,也就說只在那個綁定的實例類裏起做用)

  • 一、Component在Activity裏build,那麼這個單例只限於在這個Activity裏。意思退出這個Activity,再進這個Activity時,這個單例會被從新new
  • 二、若是Component在Application裏build,那麼這個單例就是全局的。注意這裏涉及到了咱們以前的Component依賴Component。

簡單使用介紹:
一、不適用Module的局域單例:

不使用Module,構造函數固然要加上@Inject。而後在要單例的類上加上@Singleton標註;
接下來在Component裏:在@Component上加上@Singleton。作好這些,在Activity裏使用就和以前同樣。可是此時已是局域單例了


二、使用Module的狀況:

使用Module有點相似使用第三庫,你須要單例的類,什麼都不須要加。記住是什麼標註都別加。而後在Module的@Provides上加上@Singleton,而後在@Component上加上@Singleton。便可,也是局域單例


這裏稍微來個例子,定義個Children小孩類:

public class Children {

}
複製代碼


對應Module以下

@Module
public class ActivityModule {
    @Singleton
    @Provides
    Children ProvidesChildren(){
        return new Children();
    }
}
複製代碼


對應Component以下

@Singleton
@Component(modules = ActivityModule.class)
public interface ActivityComponent {
    void inject(KstudyActivity kstudyActivity);
}
複製代碼

作完一上操做就行了。在Activity裏的操做和普通的@Inject同樣。此時chidren就在KstudyActivity是局域單例了。意思退出KstudyActivity頁面再從新進入KstudyActivity頁面,它會被從新實例化,和以前的chidren不是一個,它被從新new了。


實現全局單例

這裏爲了更加理解Dagger2,這個時候我打算把局域單例,和全局單例放在一塊兒,放在一個頁面展現,並打印他們的HashCode值。這樣更加能比較出來,看出區別。以前的局域單例代碼留着,先保持不變。

回想一下咱們以前的Component依賴Component。這個時候咱們要讓全局單例的Component在Application裏進行Build,而後提供一個方法,提供給須要使用的子頁面使用。定義超人類SurperMan

public class SurperMan {

}
複製代碼


定義全局單例Module

@Module
public class ApplicationMoule {
    @Singleton
    @Provides
    SurperMan provideSurperMan() {
        return new SurperMan();
    }
}
複製代碼


定義全局單例Component,還記得Component依賴Component的知識嗎,這裏要把SurperMan返回出去(void inject(Application application)你加上也能夠)

@Singleton
@Component(modules = ApplicationMoule.class)
public interface ApplicationComponent {
    SurperMan provideSurperMan();
}
複製代碼


在Application裏把咱們的全局單例的Component build好。而後提供一個方法,返回給子頁面

public class MyApplication extends Application {

    private ApplicationComponent applicationComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        applicationComponent = DaggerApplicationComponent.builder().build();
    }

    public ApplicationComponent getApplicationComponent() {
        return applicationComponent;
    }
}
複製代碼


重點來了,小夥伴們。首先是KstudyActivity咱們的要用咱們的全局單例。我第一篇文章也說了,一個頁面不能有多個Component去註冊,不然報錯。那麼怎麼辦呢。還記得dependencies嗎。

這個時候父Component也準備好了:ApplicationComponent

這個時候咱們以前的子Component,局域單例Component:ActivityComponent。須要依賴咱們的父Component

@Singleton
@Component(modules = ActivityModule.class,dependencies = ApplicationComponent.class)
public interface ActivityComponent {
    void inject(KstudyActivity kstudyActivity);
}
複製代碼

我先告訴你,看上去一切正常,但我告訴你會報錯:

This @Singleton component cannot depend on scoped components

這個時候我也是懵逼的,在查閱大量資料。說是局域單例和全局單例在一個頁面時,不能一塊兒用@Singleton標註。這裏我仍是不理解爲啥,有明白的小夥伴,望告知。


那麼咱們自定義@Scope,其實和@Singleton如出一轍,只不過用了咱們的類名

@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
複製代碼

而後把咱們的局域單例的子Component: ActivityComponent 和 子Module:ActivityModule 的@Singleton標註,改爲咱們的自定義Scope,@ApplicationScope。


而後看Activity裏的代碼,成果啊

public class KstudyActivity extends BaseActivity {
    //做用域只在Activity裏的單例
    @Inject
    Children children_1;
    @Inject
    Children children_2;
    //做用域在全局的單例
    @Inject
    SurperMan surperMan;
    
    @Override
    protected void processLogic() {
        ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
        DaggerActivityComponent.builder().applicationComponent(applicationComponent)
                .build().inject(this);
        //全局單例
        LogUtils.i("看看hashCode值", "做用域在activity裏的單例 children_1 hashCode ==> " + children_1.hashCode());
        LogUtils.i("看看hashCode值", "做用域在activity裏的單例 children_2 hashCode ==> " + children_2.hashCode());
        LogUtils.i("看看hashCode值", "做用域在Application裏的全局單例 surperMan hashCode ==> " + surperMan.hashCode() + "");

    }
}
複製代碼


最後一步,咱們進2次KstudyActivity頁面,以紅線分割,看打印,打印以下:

是否是明顯了,Children是局域單例,每次進頁面都會被實例化。SurperMan是全局單例,hashCode值一直不會變。這個時候若是你還不清楚,我以前說了看Component在哪build:


咱們的SurperMan的ApplicationComponent,在Application裏build的

@Override
    public void onCreate() {
        super.onCreate();
        applicationComponent = DaggerApplicationComponent.builder().build();
    }
複製代碼


而咱們的Children的ActivityComponent,在哪build呢

protected void processLogic() {
        ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
        DaggerActivityComponent.builder().applicationComponent(applicationComponent)
                .build().inject(this);
    }
複製代碼

明白了嗎?不明白再簡化,是否是在Activity裏build的

protected void processLogic() {
        ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
        ActivityComponent activityComponent  = DaggerActivityComponent.builder().applicationComponent(applicationComponent)
                .build();
        activityComponent.inject(this)
    }
複製代碼

四、Provider和Lazy的介紹和用法以及和@Inject的區別

Dagger2在Java中的使用,在我總結裏,是最後一個知識點了。

首先咱們看下定義3個類,我這裏區分便於理解:貓,豬,羊。裏面都同樣,仍是貼下代碼。更清晰,都跟貓這樣:

public class Cat {
}
複製代碼


而後是Module

@Module
public class AnimModule {
    @Provides
    Cat catProvides() {
        return new Cat();
    }

    @Provides
    Pig pigProvides() {
        return new Pig();
    }

    @Provides
    Sheep sheepProvides() {
        return new Sheep();
    }
}
複製代碼


而後是Component

@Component(modules = AnimModule.class)
public interface AnimComponent {
    void inject(LstudyActivity lstudyActivity);
}
複製代碼


在Activity裏咱們打印他們的haschCode值

public class LstudyActivity extends BaseActivity {
    //使用@Inject正常的。
    @Inject
    Cat cat_1;
    @Inject
    Cat cat_2;

    //使用Provider
    @Inject
    Provider<Pig> pig_1;
    @Inject
    Provider<Pig> pig_2;

    //使用Lazy
    @Inject
    Lazy<Sheep> sheep_1;
    @Inject
    Lazy<Sheep> sheep_2;

    @Override
    protected void processLogic() {
        DaggerAnimComponent.builder().build().inject(this);
        LogUtils.i("看看hashCode值", "@Inject cat_1的hashCode ==> " + cat_1.hashCode());
        LogUtils.i("看看hashCode值", "@Inject cat_2的hashCode ==> " + cat_2.hashCode());
        LogUtils.i("看看hashCode值", "Provider pig_1的hashCode ==> " + pig_1.hashCode());
        LogUtils.i("看看hashCode值", "Provider pig_2的hashCode ==> " + pig_2.hashCode());
        LogUtils.i("看看hashCode值", "Lazy sheep_1的hashCode ==> " + sheep_1.hashCode());
        LogUtils.i("看看hashCode值", "Lazy sheep_2的hashCode ==> " + sheep_2.hashCode());
    }
}
複製代碼


我一樣進2次頁面,以紅線分割,看結果

首先咱們能夠看到這裏的進2次頁面,全部的hashCode都發生了變化,就是紅線上和紅線下一一對比,都發生改變了。


先別急,這裏還看不出區別,由於Provider和Lazy尚未調用get(),咱們單獨,點擊打印。看看狀況: Activity裏的代碼

public class LstudyActivity extends BaseActivity {
    @Inject
    Provider<Pig> pig_spec;
    @Inject
    Lazy<Sheep> sheep_spec;

    @OnClick(R.id.btn)
    public void specOnClick() {
        LogUtils.i("看看hashCode值", "Provider pig_spec不經過get的hashCode ==> " + pig_spec.hashCode());
        LogUtils.i("看看hashCode值", "Provider pig_spec的hashCode ==> " + pig_spec.get().hashCode());
        LogUtils.i("看看hashCode值", "Lazy sheep_spec的hashCode ==> " + sheep_spec.get().hashCode());
    }
}
複製代碼


咱們點擊2次按鈕,以紅線分割2次點擊結果。看結果

  • 能夠看到不調用get()方法的Provider的的HashCode值。一直不變
  • 調用get()方法的Provider的HashCode值,一直在改變
  • 調用get()方法的Lazy的HashCode值,一直不變。

咱們回看上一張圖。provider修飾的pig_一、pig_2。不調用get()的是一直不變的。Lazy修飾的sheep_1,sheep_2,不調用get()方法。確是一直在變得。到底怎麼理解呢

Provider和Lazy和@Inject的總結:

一、@Inject:在依賴注入對象的時候,每次都是實例化的。

二、Lazy:官方介紹是懶加載,調用.get()。才生成實例,並且一旦實例生成,在調用。會一直使用已經生成的實例。你去看生成的代碼, Lazy也是Provider的一種,只不過用了LstudyActivity_MembersInjector.injectSheep_1(instance, DoubleCheck.lazy(sheepProvidesProvider));
  那爲何咱們第一張圖,不調用get()方法的Lazy修飾的sheep_1和sheep_2的HashCode值,確不同呢。這個時候咱們看下代碼

@Inject
    Lazy<Sheep> sheep_1; //這裏不經過get()方法,打印的不是Sheep實例,而是Lazy<Sheep>容器的hashCode
    @Inject
    Lazy<Sheep> sheep_2;
複製代碼

是否是明白了。調用get(),方法纔會生成Sheep實例。這裏的標註只是Lazy<T>,因此說這裏定義了2個容器,因此纔出現不調用get()方法的容器hashCode值纔會不同,調用了get()方法纔會同樣

三、Provider:實際上是一種工廠模式,每次調用.get()。都會生成不一樣的實例;不調用不會生成實例。那麼爲何不調用get()方法,hashCode反而同樣呢

//這裏同樣,不調用get()方法是容器Provider<T>,說是容器,這裏更能夠說成是工廠
//由於每次調用get()方法,都實例化Pig的實例。因此這裏只須要一個工廠(我是這麼理解的)
@Inject
Provider<Pig> pig_1; 
複製代碼

結束語:在Java裏使用Dagger2仍是很麻煩的。下片介紹在Android中使用,你會瞬間愛上Dagger2

本文github Demo地址

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

相關文章
相關標籤/搜索