Dagger2 小結(jié)

Dagger2是Google在Square開源的Dagger的基礎(chǔ)上將反射注入的方式修改到編譯時而得到的依賴注入框架没讲。
官方地址:GitHub
官方文檔:https://google.github.io/dagger/

四個關(guān)鍵注解:

@Inject

用于類構(gòu)造器和類的屬性灭忠。

  • 用在類的構(gòu)造器上用來注解提供對象的方法,Dagger會根據(jù)該構(gòu)造器來生成該類的工廠類ClassName_Factory。工廠類實(shí)現(xiàn)了Factory<T>接口栈妆,通過覆寫的get()方法調(diào)用注解的構(gòu)造方法提供類的對象:
public final class Noodle_Factory implements Factory<Noodle> {
  private static final Noodle_Factory INSTANCE = new Noodle_Factory();

  @Override
  public Noodle get() {
    return new Noodle();
  }

  public static Factory<Noodle> create() {
    return INSTANCE;
  }

  /** Proxies {@link Noodle#Noodle()}. */
  public static Noodle newNoodle() {
    return new Noodle();
  }
}

原始類:

class Noodle {

    @Inject
    public Noodle() {
    }

    @Override
    public String toString() {
        return "面條";
    }
}
  • 用在類的屬性上用來注解該屬性需要依賴注入。Dagger會為根據(jù)該類需要注入的屬性生成ClassName_MembersInjector注入類厢钧,該類實(shí)現(xiàn)了MembersInjector<T>接口鳞尔。通過靜態(tài)的create(arg...)方法來生成實(shí)例,該方法的參數(shù)類型為需要注入的屬性對應(yīng)的Provider<T>類型(Factory<T>Provider<T>的子類型)早直,參數(shù)個數(shù)與需要注入的字段個數(shù)一致寥假。注入類通過覆寫的injectMembers(T instance)方法來注入目標(biāo)類需要的屬性:
public final class ZhaiNan_MembersInjector implements MembersInjector<ZhaiNan> {
  private final Provider<Baozi> baoziProvider;

  private final Provider<Noodle> noodleProvider;

  public ZhaiNan_MembersInjector(Provider<Baozi> baoziProvider, Provider<Noodle> noodleProvider) {
    assert baoziProvider != null;
    this.baoziProvider = baoziProvider;
    assert noodleProvider != null;
    this.noodleProvider = noodleProvider;
  }

  public static MembersInjector<ZhaiNan> create(
      Provider<Baozi> baoziProvider, Provider<Noodle> noodleProvider) {
    return new ZhaiNan_MembersInjector(baoziProvider, noodleProvider);
  }

  @Override
  public void injectMembers(ZhaiNan instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.baozi = baoziProvider.get();
    instance.noodle = noodleProvider.get();
  }

  public static void injectBaozi(ZhaiNan instance, Provider<Baozi> baoziProvider) {
    instance.baozi = baoziProvider.get();
  }

  public static void injectNoodle(ZhaiNan instance, Provider<Noodle> noodleProvider) {
    instance.noodle = noodleProvider.get();
  }
}

若一個類自身需要為別的類提供依賴(他的構(gòu)造器用@Inject注解),同時也需要別的類提供依賴(他的屬性用@Inject注解)霞扬,他對應(yīng)的工廠類會依賴于他對應(yīng)的MembersInjector糕韧,并調(diào)用MembersInjectors.injectMembers()來為自身注入依賴:

public final class ZhaiNan_Factory implements Factory<ZhaiNan> {
  private final MembersInjector<ZhaiNan> zhaiNanMembersInjector;

  public ZhaiNan_Factory(MembersInjector<ZhaiNan> zhaiNanMembersInjector) {
    assert zhaiNanMembersInjector != null;
    this.zhaiNanMembersInjector = zhaiNanMembersInjector;
  }

  @Override
  public ZhaiNan get() {
    return MembersInjectors.injectMembers(zhaiNanMembersInjector, new ZhaiNan());
  }

  public static Factory<ZhaiNan> create(MembersInjector<ZhaiNan> zhaiNanMembersInjector) {
    return new ZhaiNan_Factory(zhaiNanMembersInjector);
  }
}

原始類:

public class ZhaiNan {

    @Inject
    Baozi baozi;

    @Inject
    Noodle noodle;

    @Inject
    public ZhaiNan() {
    }

    public String eat() {
        StringBuilder sb = new StringBuilder();
        sb.append("我吃的是 ");
        if (baozi != null) {
            sb.append(baozi.toString());
        }

        if (noodle != null) {
            sb.append("  ");
            sb.append(noodle.toString());
        }
        return sb.toString();
    }

}

class Noodle {

    @Inject
    public Noodle() {
    }

    @Override
    public String toString() {
        return "面條";
    }
}

@Module 和 @Provides

上面介紹了Dagger生成的核心類,即為被注入的屬性生成的工廠類以及注入類喻圃,工廠類用來提供該屬性需要的對象萤彩,注入類提供將該對象綁定要屬性需要的方法。生成工廠類需要使用@Inject注解提供依賴的類的構(gòu)造函數(shù)斧拍,但現(xiàn)實(shí)情況是雀扶,我們很多時候沒法修改該類,這個時候我們就需要手動實(shí)現(xiàn)提供該該類的對象的方法饮焦。使用@Module@Provides來解決這個問題怕吴。@Module 用來注解我們自己實(shí)現(xiàn)的工廠類窍侧,@Provides用來注解里面的工廠方法县踢,Dagger會為每一個工廠方法生成一個工廠類。假如@Module標(biāo)注的類名為ZhaiNanModule伟件,@Provides標(biāo)注的方法名為provideBaozi()硼啤,生成的工廠類則為:

public final class ZhaiNanModule_ProvideBaoziFactory implements Factory<Baozi> {
  private final ZhaiNanModule module;

  public ZhaiNanModule_ProvideBaoziFactory(ZhaiNanModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public Baozi get() {
    return Preconditions.checkNotNull(
        module.provideBaozi(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<Baozi> create(ZhaiNanModule module) {
    return new ZhaiNanModule_ProvideBaoziFactory(module);
  }
}

Module 類:

@Module
public class ZhaiNanModule {

    @Provides
    public ZhaiNan provideZaiNan() {
        return new ZhaiNan();
    }

    @Provides
    public Noodle provideNoodle() {
        return new Noodle();
    }

    @Provides
    public Baozi provideBaozi() {
        return new Baozi();
    }
}

注意:

  • @Provides標(biāo)注的方法中直接new 出來的對象,即使該對象內(nèi)部有需要被注入的屬性斧账,Dagger也沒法自動去完成依賴注入谴返。即Dagger不會為new出來的對象自動注入依賴。
  • 若提供依賴的類的構(gòu)造方法使用了@Inject注解咧织,并且@Module注解的類也中提供了返回該類的方法嗓袱,Dagger會優(yōu)先使用@Module注解的類中的方法。

@Component()

依賴提供方和依賴需求方的紐帶习绢。@Component()注解的接口或抽象類中定義提供已經(jīng)注入好依賴的對象的方法(前提是Module中或Dagger生成的代碼中提供該對象的方法會注入該對象的依賴)或者為對象注入依賴方法渠抹。被它注解的接口中定義的方法若為返回一個對象蝙昙,則該方法執(zhí)行完后返回對象中定義的所有需要被注入的依賴都會被注入好,若該方法接受一個對象作為參數(shù)梧却,則該方法執(zhí)行完后該對象內(nèi)需要被注入的屬性都會被注入好奇颠。
若接口名稱為ZhaiNanComponent,則Dagger生成的類名為DaggerZhaiNanComponent放航,并且實(shí)現(xiàn)了接口中定義的方法:
原始類:

@Component(modules = {ZhaiNanModule.class}) // 指定提供依賴方法的模塊
public interface ZhaiNanComponent {
    ZhaiNan get();

    void inject(MainActivity mainActivity);

    void inject(ZhaiNan zhaiNan);
}

生成的類:

public final class DaggerZhaiNanComponent implements ZhaiNanComponent {
  private Provider<ZhaiNan> provideZaiNanProvider;

  private MembersInjector<MainActivity> mainActivityMembersInjector;

  private Provider<Baozi> provideBaoziProvider;

  private Provider<Noodle> provideNoodleProvider;

  private MembersInjector<ZhaiNan> zhaiNanMembersInjector;

  private DaggerZhaiNanComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static ZhaiNanComponent create() {
    return new Builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideZaiNanProvider = ZhaiNanModule_ProvideZaiNanFactory.create(builder.zhaiNanModule);

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideZaiNanProvider);

    this.provideBaoziProvider = ZhaiNanModule_ProvideBaoziFactory.create(builder.zhaiNanModule);

    this.provideNoodleProvider = ZhaiNanModule_ProvideNoodleFactory.create(builder.zhaiNanModule);

    this.zhaiNanMembersInjector =
        ZhaiNan_MembersInjector.create(provideBaoziProvider, provideNoodleProvider);
  }

  @Override
  public ZhaiNan get() {
    return provideZaiNanProvider.get();
  }

  @Override
  public void inject(MainActivity mainActivity) {
    mainActivityMembersInjector.injectMembers(mainActivity);
  }

  @Override
  public void inject(ZhaiNan zhaiNan) {
    zhaiNanMembersInjector.injectMembers(zhaiNan);
  }

  public static final class Builder {
    private ZhaiNanModule zhaiNanModule;

    private Builder() {}

    public ZhaiNanComponent build() {
      if (zhaiNanModule == null) {
        this.zhaiNanModule = new ZhaiNanModule();
      }
      return new DaggerZhaiNanComponent(this);
    }

    public Builder zhaiNanModule(ZhaiNanModule zhaiNanModule) {
      this.zhaiNanModule = Preconditions.checkNotNull(zhaiNanModule);
      return this;
    }
  }
}

現(xiàn)在我們可以這么使用該類:

@Inject
ZhaiNan zhaiNan;

// 為對象注入依賴
ZhaiNanComponent zhaiNanComponent = DaggerZhaiNanComponent.builder()
        .zhaiNanModule(new ZhaiNanModule())
        .build();
zhaiNanComponent.inject(this); // 執(zhí)行后zhaiNan就會被賦值
zhaiNanComponent.inject(zhaiNan); // 要執(zhí)行這一步烈拒,因?yàn)閦haiNan在Module里是new出來的

// 獲取一個依賴被注入完畢的對象
zhaiNan = DaggerZhaiNanComponent.builder().build().get();

注意:若Module中提供依賴的方法為static,則可以用ZhaiNanComponent zhaiNanComponent = DaggerZhaiNanComponent.create();來創(chuàng)建Component

輔助性注解

@Singleton 和 @Scope

@Singleton@Scope被注解的一個注解广鳍,用來和@Provides注解一起注解提供依賴的方法荆几,被@Singleton注解的方法能在@Component范圍內(nèi)提供單例(該Component同時也需要被@Singleton注解)。我們可以直接使用@Singleton搜锰,也可以像這樣自定義一個局部單例注解:

@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ActivityScoped {
}

其中@Scope是必須的伴郁,其他可選。現(xiàn)在我們可以使用@ActivityScoped去注解@Provides方法和@Component類蛋叼,來指定在該@Component@Provides方法返回同一個對象實(shí)例焊傅。使用@ActivityScoped注解上面的provideBaozi()方法后,生成的DaggerZhaiNanComponent類的private void initialize(final Builder builder)方法中生成provideBaoziProvider方式變成了:

// 原來是
//this.provideBaoziProvider = ZhaiNanModule_ProvideBaoziFactory.create();
this.provideBaoziProvider = DoubleCheck.provider(ZhaiNanModule_ProvideBaoziFactory.create());

使用了DoubleCheck<T>類來生成了一個代理狈涮,通過DoubleCheck類覆寫的get() 方法來提供單例狐胎,所以它能實(shí)現(xiàn)Component級別的單例。

@Named 和 @Qualifiers

當(dāng)你需要在Module中定義多個返回同類型的方法的時候歌馍,@name就派上用場了握巢。@name@Qualifiers注解的一個注解,用來注解@Provides注解的提供依賴對象的方法松却,和@Inject注解的需要被注入的對象暴浦,以區(qū)分使用哪個方法來提供實(shí)例:

@Module
public class ZhaiNanModule {
    @Provides
    @Named("baozi1")
    public static Baozi provideBaozi1() {
        return new Baozi();
    }

    @Provides
    @Named("baozi2")
    public static Baozi provideBaozi2() {
        return new Baozi();
    }
}

public class MainActivity extends AppCompatActivity {

    @Inject
    ZhaiNan zhaiNan;

    @Inject
    @Named("baozi1")
    Baozi baozi;

    @Inject
    @Named("baozi2")
    Baozi baozi2;
}

生成的DaggerZhaiNanComponent類的private void initialize(final Builder builder)方法中生成mainActivityMembersInjector方式變成了:

this.mainActivityMembersInjector =
        MainActivity_MembersInjector.create(
            ZhaiNanModule_ProvideZaiNanFactory.create(),
            ZhaiNanModule_ProvideBaozi1Factory.create(),
            ZhaiNanModule_ProvideBaozi2Factory.create());

若不想寫每次寫個name,可以自定義一個注解:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Baozi1 {
}

延遲加載和強(qiáng)制重新加載

分別使用Lazy<T>Provider<T>類來實(shí)現(xiàn):

public class Test {

    @Inject
    @Named("Test")
    Lazy<String> name;

    @Inject
    Provider<Integer> randomValue;

    public String getName() {
        return name.get();
    }

    public int getRandomValue() {
        return randomValue.get().intValue();
    }
}

這樣當(dāng)?shù)谝淮握{(diào)用getName()時晓锻,我們需要的String對象才被創(chuàng)建歌焦;而每次調(diào)用getRandomValue()時,一個新的Integer對象都會被創(chuàng)建砚哆。

Component依賴

若一個Component需要依賴另一個独撇,可以通過指定@Component(modules = XModule.class, dependencies = OtherComponent.class),這樣就可以使用OtherComponent定義的方法躁锁。在創(chuàng)建Component的時候把依賴的Component傳入:

XiaoChiComponent xiaoChiComponent = DaggerXiaoChiComponent.builder()
        .build();

DaggerFoodComponent.builder()
        .xiaoChiComponent(xiaoChiComponent)
        .build()
        .inject(this);

SubComponent

Component依賴類似于組合纷铣,而SubComponent類似于繼承:

@Subcomponent(modules = FoodModule.class)
public interface SubComponent {
    void inject(ThirdActivity activity);
}

@Component(modules = XiaoChiModule.class)
public interface ParentComponent {
    SubComponent provideSubComponent();
}

DaggerParentComponent.builder().build()
                .provideSubComponent().inject(this);

參考

輕松學(xué),聽說你還沒有搞懂 Dagger2

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末战转,一起剝皮案震驚了整個濱河市搜立,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌槐秧,老刑警劉巖啄踊,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寸潦,死亡現(xiàn)場離奇詭異,居然都是意外死亡社痛,警方通過查閱死者的電腦和手機(jī)见转,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒜哀,“玉大人斩箫,你說我怎么就攤上這事∧於” “怎么了乘客?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長淀歇。 經(jīng)常有香客問我易核,道長,這世上最難降的妖魔是什么浪默? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任牡直,我火速辦了婚禮,結(jié)果婚禮上纳决,老公的妹妹穿的比我還像新娘碰逸。我一直安慰自己,他們只是感情好阔加,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布饵史。 她就那樣靜靜地躺著,像睡著了一般胜榔。 火紅的嫁衣襯著肌膚如雪胳喷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天夭织,我揣著相機(jī)與錄音吭露,去河邊找鬼。 笑死摔癣,一個胖子當(dāng)著我的面吹牛奴饮,可吹牛的內(nèi)容都是我干的纬向。 我是一名探鬼主播择浊,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逾条!你這毒婦竟也來了琢岩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤师脂,失蹤者是張志新(化名)和其女友劉穎担孔,沒想到半個月后江锨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡糕篇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年啄育,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拌消。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡挑豌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出墩崩,到底是詐尸還是另有隱情氓英,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布鹦筹,位于F島的核電站铝阐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏铐拐。R本人自食惡果不足惜徘键,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遍蟋。 院中可真熱鬧啊鸭,春花似錦、人聲如沸匿值。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挟憔。三九已至钟些,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绊谭,已是汗流浹背政恍。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留达传,地道東北人篙耗。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像宪赶,于是被迫代替她去往敵國和親宗弯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容