有關(guān)Dagger2的一些事(一)

這是為Dagger2的詳細(xì)分析第一篇,以做記錄答恶。
此篇介紹以下內(nèi)容:

  • @Inject
  • @Component
  • @Module
  • @Scope
  • @SubComponent
  • denpendency
  • @Lazy
  • @Binds
  • @IntoSet
  • @IntoMap

此篇不會(huì)從頭開(kāi)始介紹dagger2,假設(shè)讀者已經(jīng)對(duì)dagger2有一定的了解哈

基本用法--沒(méi)有Module

  1. 構(gòu)造函數(shù)上標(biāo)注@Inject
public class A {
    @Inject
    public A() {}

}
  1. 定義Component作為連接器
@Component
public interface ActivityComponent {
     void inject(MainActivity MainActivity);
}
  1. 要進(jìn)行注入的Activity
public class MainActivity extends Activity implements LifecycleOwner
{
    
    @Inject
    A a;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        DaggerActivityComponent.builder().build().inject(this);
    }
}

1.png

我們?cè)敿?xì)看生成了什么:
對(duì)于ActivityComponent,dagger會(huì)幫你生成DaggerxxxComponent
對(duì)于MainActivity,dagger會(huì)幫你生成xxxMemberInjector

public final class DaggerActivityComponent implements ActivityComponent {
  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

  public static ActivityComponent create() {
    return builder().build();
  }

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

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(A_Factory.create());
  }

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

  public static final class Builder {
    private Builder() {}

    public ActivityComponent build() {
      return new DaggerActivityComponent(this);
    }
  }
}

兩行關(guān)鍵代碼:

this.mainActivityMembersInjector = MainActivity_MembersInjector.create(A_Factory.create());
mainActivityMembersInjector.injectMembers(MainActivity);

Factory是真正提供數(shù)據(jù)的工廠,Injector包含Factory,所以Injector可以向變量注入實(shí)例。

@Module

  1. 不在構(gòu)造函數(shù)上標(biāo)注@Inject,而是新生成一個(gè)Module
@Module
public class ActivityModule {

    @Provides
    public A provideA(){
        return new A();
    }
}

這是什么意思呢,就是需要實(shí)例時(shí)不會(huì)再?gòu)臉?gòu)造函數(shù)中去獲取,而是通過(guò)providexxx獲取。

  1. 定義Component作為連接器,注意注解參數(shù)與前面的區(qū)別
@Component(modules = ActivityModule.class)
public interface ActivityComponent {
     void inject(MainActivity MainActivity);
}

MainActivity中:

// 構(gòu)造橋梁對(duì)象
DaggerActivityComponent.builder()
        .activityModule(new ActivityModule())
        .build()
        .inject(this);

注意這里.activityModule(new xxx)不寫(xiě)也沒(méi)事,dagger會(huì)自動(dòng)幫我們構(gòu)建,當(dāng)然如果ActivityModule的構(gòu)造函數(shù)需要傳遞參數(shù)就需要我們手動(dòng)寫(xiě)了验毡。

2.png

注入過(guò)程

  • 查找Module中是否存在創(chuàng)建該類的方法坦喘。
  • 若存在創(chuàng)建類方法,查看該方法是否存在參數(shù)
    • 若存在參數(shù)采幌,則按從步驟1開(kāi)始依次初始化每個(gè)參數(shù)
    • 若不存在參數(shù)贴届,則直接初始化該類實(shí)例,一次依賴注入到此結(jié)束
  • 若不存在創(chuàng)建類方法蜡吧,則查找Inject注解的構(gòu)造函數(shù)毫蚓,看構(gòu)造函數(shù)是否存在參數(shù)
    • 若存在參數(shù),則從步驟1開(kāi)始依次初始化每個(gè)參數(shù)
    • 若不存在參數(shù)昔善,則直接初始化該類實(shí)例元潘,一次依賴注入到此結(jié)束

@Scope

一個(gè)很具有疑惑性的注解。

  1. 創(chuàng)建注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}
  1. 在Module或構(gòu)造函數(shù)上標(biāo)注
@Module  
public class UserModule {  
    ...
    @Provides  
    @UserScope
    User providesUser() {  
        return new A();  
    }  
  
}  

or

@UserScope 
public class User {  
    @Inject  
    public User() {  
    } 
}  
  1. 在使用該作用于的Component上標(biāo)注
@UserScope
@Component(modules = {UserModule.class})
public interface UserComponent {
   ...
}

先說(shuō)一下這個(gè)注解到底是什么意思君仆。經(jīng)臭娓牛看到什么@Singleton|@PerActivity|PerFragment。那些背后都是靠一個(gè)@Scope完成的返咱。那這個(gè)作用域是由誰(shuí)控制的, 其實(shí)就是靠Component控制的钥庇。
即:Component活多久,這個(gè)注解生成的實(shí)例就活多久咖摹。

我們以常見(jiàn)的PerActivity為例,為什么它能做到跟Activity一個(gè)生命周期,就是因?yàn)槟忝啃律梢粋€(gè)Activity,你的DaggerxxxComponent都會(huì)由你重新.build生成评姨。
假設(shè)我們?cè)?code>Application中生成一個(gè)component,并且在每個(gè)Activity里通過(guò)getApplication().getComponent()去獲取Component,那么不好意思,你的實(shí)例就和Application同生死了萤晴。

所以這個(gè)是怎么做到的,很簡(jiǎn)單,背后利用了DoubleCheck

DoubleCheck:

Provider外面包一層DoubleCheck,每次get時(shí)看當(dāng)前Component下有沒(méi)有已經(jīng)生成的實(shí)例,有的話就直接返回吐句。



  @SuppressWarnings("unchecked") // cast only happens when result comes from the provider
  @Override
  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          /* Get the current instance and test to see if the call to provider.get() has resulted
           * in a recursive call.  If it returns the same instance, we'll allow it, but if the
           * instances differ, throw. */
          Object currentInstance = instance;
          if (currentInstance != UNINITIALIZED && currentInstance != result) {
            throw new IllegalStateException("Scoped provider was invoked recursively returning "
                + "different results: " + currentInstance + " & " + result);
          }
          instance = result;
          /* Null out the reference to the provider. We are never going to need it again, so we
           * can make it eligible for GC. */
          provider = null;
        }
      }
    }
    return (T) result;
  }

很簡(jiǎn)單就實(shí)現(xiàn)了作用域的控制。我的理解就是這么簡(jiǎn)單店读。

dependency

@Component(modules = ActivityModule.class)
public interface AComponent {
     A provideA();
}

@Component(dependencies = AComponent.class)
public interface BComponent {

    void inject(MainActivity MainActivity);
  
}

DaggerBComponent.builder().activityComponent(DaggerAComponent.builder().activityModule(new ActivityModule()).build()).build().inject(this);

先看上面代碼,有兩個(gè)Component嗦枢。B是我們常見(jiàn)的寫(xiě)法,但是它@Component(dependencies = ActivityComponent.class)依賴于A。

A只暴露了一個(gè)接口A provideA();

所以最終實(shí)現(xiàn)什么效果呢,B可以使用A提供的provideA去注入A的實(shí)例屯断。

4.png
  • 父Component中要顯式的寫(xiě)出需要暴露可提供給子Component的依賴文虏;

  • 子Component在注解中使用dependencies=來(lái)連接父Component侣诺;

@SubComponent


@Component(modules = ActivityModule.class)
public interface ActivityComponent {
     AnotherComponent anotherComponent();
}

@Subcomponent
public interface AnotherComponent {

    void inject(MainActivity MainActivity);

}

DaggerActivityComponent.builder().build().anotherComponent(DaggerAnotherComponent.builder().build()).inject(this);

我們會(huì)看到,它跟denpendency不同,后者會(huì)生成兩個(gè)DaggerxxxComponent,但它只會(huì)生成一個(gè)。



public final class DaggerActivityComponent implements ActivityComponent {
  ...


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

    this.provideAProvider = ActivityModule_ProvideAFactory.create(builder.activityModule);
  }

  @Override
  public AnotherComponent anotherComponent() {
    return new AnotherComponentImpl();
  }

  public static final class Builder {
    private ActivityModule activityModule;

    private Builder() {}

    public ActivityComponent build() {
      if (activityModule == null) {
        this.activityModule = new ActivityModule();
      }
      return new DaggerActivityComponent(this);
    }

    public Builder activityModule(ActivityModule activityModule) {
      this.activityModule = Preconditions.checkNotNull(activityModule);
      return this;
    }
  }

  private final class AnotherComponentImpl implements AnotherComponent {
    private MembersInjector<MainActivity> mainActivityMembersInjector;

    private AnotherComponentImpl() {
      initialize();
    }

    @SuppressWarnings("unchecked")
    private void initialize() {

      this.mainActivityMembersInjector =
          MainActivity_MembersInjector.create(DaggerActivityComponent.this.provideAProvider);
    }

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

先說(shuō)一個(gè)事實(shí)择葡,一個(gè)Component要不只能是@Component,要不只能是@SubComponent

然后看上面代碼,最終是由標(biāo)注了@SubComponent的AnotherComponent去進(jìn)行了真正的注入紧武。這里@SubComponent可以繼承它的父類提供的所有實(shí)例。我試了一下,如果只有AnotherComponent存在是無(wú)法完成注入的敏储,也就是說(shuō)他是不獨(dú)立的阻星,它必須依附于父類。

所以dependencysubComponent的區(qū)別:

Component Dependencies - Use this when:

you want to keep two components independent.
you want to explicitly show what dependencies from one component is used by the other

Subcomponents - Use this when:
you want to keep two component cohesive
you may not care to explicitly show what dependencies from one component is used by the other

前者更獨(dú)立,組件只是提供provide給另一個(gè)借用一下已添。

同時(shí)注意:

兩個(gè)擁有依賴關(guān)系的 Component 是不能有相同 @Scope 注解的妥箕!使用@SubComponent 則可以使用相同的@Scope注解。 (很重要8琛F璐薄!@虏酢)

@Lazy & @Provider

Activity中宇葱,我們可以這么聲明變量。

@Inject
Lazy<A> a;
Provider<A> b;

只有真正去aLazy.get() 才會(huì)進(jìn)行注入刊头。

實(shí)現(xiàn)也很簡(jiǎn)單,繼續(xù)DoubleCheck包裹Provider,然后真正get的時(shí)候才會(huì)從Factory中獲取黍瞧。不過(guò)他跟前面的scope不大一樣,是在對(duì)instance實(shí)例賦值的時(shí)候進(jìn)行包裹的。

其中Lazy(懶加載)的作用好比component初始化了一個(gè)present對(duì)象,然后放到一個(gè)池子里,需要的時(shí)候就get它,所以你每次get的時(shí)候拿到的對(duì)象都是同一個(gè);并且當(dāng)你第一次去get時(shí),它才會(huì)去初始化這個(gè)實(shí)例.

  @Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.a = DoubleCheck.lazy(bAndAProvider);
    instance.b = bAndAProvider;
  }

而對(duì)于b直接賦予provider的值,后面要get時(shí)就從provider.get獲取原杂。

procider(強(qiáng)制加載)的作用:

1:同上當(dāng)你第一次去get時(shí),它才會(huì)去初始化這個(gè)實(shí)例

2:后面當(dāng)你去get這個(gè)實(shí)例時(shí),依舊是從Provider中獲取,所以跟基本用法是一樣的印颤。

所以Provider與Lazy的區(qū)別在于,Lazy延遲加載之后每次的調(diào)用都是同一個(gè)對(duì)象,而Provider只是延遲加載,至于調(diào)用的對(duì)象是不是同一個(gè)就要看@scope有沒(méi)有進(jìn)行約束了。

@Binds

現(xiàn)在我們假設(shè)有個(gè)接口叫IinterfaceA

UserAUserB分別實(shí)現(xiàn)了這個(gè)接口穿肄。

然后我們?cè)?code>Activity里這么寫(xiě):

 @Inject
 IinterfaceA a;

我們希望實(shí)現(xiàn)IinterfaceA a = new UserA()

我們不能直接在UserA的構(gòu)造函數(shù)上標(biāo)注@Inject因?yàn)槲覀円⑷氲氖墙涌诓皇菍?shí)現(xiàn)年局。所以按已有的知識(shí)體系那就用Module咯:

  @Provides
  public IinterfaceA providesA(){
    return new UserA();
  }

而如果用@Binds這個(gè)新的特性,我們可以這么寫(xiě)。注意abstract

@Module
public abstract class AModule {

  @Binds
  public abstract IinterfaceA bindA(UserA userA);
}

這個(gè)意思是如果你想要注入到IinterfaceA,請(qǐng)找UserA咸产。

其實(shí)這兩貨看起來(lái)差不多,那為什么要出現(xiàn)@Binds矢否。后來(lái)看有個(gè)解釋挺不錯(cuò):

@Provides methods are instance methods and they need an instance of our module in order to be invoked. If our Module is abstract and contains @Binds methods, dagger will not instantiate our module and instead directly use the Provider of our injected parameter (LoginPresenter in the above case).

對(duì)于Provides而言,Module是會(huì)被實(shí)例化的,因?yàn)橐?chuàng)建UserA。但是對(duì)于@Binds而言,不需要實(shí)例化Module就可以做到脑溢。

所以@Binds解決了我們面向接口編程的需求兴喂。

我的理解,@Binds是完全可以被替代的,就看你自己的需求了。

@IntoSet & @ElementsIntoSet

如果我們?cè)?code>Activity里:


@Inject
Set<String> a

這個(gè)可以通過(guò)@IntoSet & @ElementsIntoSet實(shí)現(xiàn)實(shí)例一一注入到集合中,看個(gè)例子


 @Provides @IntoSet
 String providexxx() {
    return "ABC";
 }

 @Provides @IntoSet
 String providekkk() {
    return "Acc";
 }
 
 @Provides @ElementsIntoSet
 Set<String> provideSomeStrings() {
    return new HashSet<String>(Arrays.asList("DEF", "GHI"));
 }
    

這樣a就包含<"ABC","Acc","DEF","GHI">

其實(shí)到這里基本的套路都差不多,直接上編譯出來(lái)的源碼:

   //(int individualProviderSize, int collectionProviderSize)

   this.setOfStringProvider =
        SetFactory.<String>builder(2, 1)
            .addProvider(providexxxProvider)
            .addProvider(providekkkProvider)
            .addCollectionProvider(provideSomeStringsProvider)
            .build();


  @Override
  public Set<T> get() {
    int size = individualProviders.size();
    // Profiling revealed that this method was a CPU-consuming hotspot in some applications, so
    // these loops were changed to use c-style for.  Versus enhanced for-each loops, C-style for is
    // faster for ArrayLists, at least through Java 8.

    List<Collection<T>> providedCollections =
        new ArrayList<Collection<T>>(collectionProviders.size());
    for (int i = 0, c = collectionProviders.size(); i < c; i++) {
      Collection<T> providedCollection = collectionProviders.get(i).get();
      size += providedCollection.size();
      providedCollections.add(providedCollection);
    }

    Set<T> providedValues = newHashSetWithExpectedSize(size);
    for (int i = 0, c = individualProviders.size(); i < c; i++) {
      providedValues.add(checkNotNull(individualProviders.get(i).get()));
    }
    for (int i = 0, c = providedCollections.size(); i < c; i++) {
      for (T element : providedCollections.get(i)) {
        providedValues.add(checkNotNull(element));
      }
    }

    return unmodifiableSet(providedValues);
  }

我們看到,individualProviderSize對(duì)應(yīng)@IntoSet的個(gè)數(shù),collectionProviderSize對(duì)應(yīng)@ElementsIntoSet的個(gè)數(shù)焚志。
當(dāng)get時(shí)會(huì)通過(guò)individualProvidercollectionProvider提供的數(shù)據(jù)注入到instance中形成集合衣迷。

@IntoMap

IntoSet一個(gè)性質(zhì),只不過(guò)多了Key的約束。上例子:



@Provides
@IntoMap // 指定該@Provides方法向Map提供元素
@StringKey("A") // 指定該元素在Map中所對(duì)應(yīng)的的Key
String providexxx() {
    return "ABC";

}

@Provides
@IntoMap // 指定該@Provides方法向Map提供元素
@ClassKey(MainActivity.class)
String providekkk() {
    return "Acc";

}
    

StringKey代表以字符串作為key,ClassKey代表以類作為key酱酬。
目前dagger2預(yù)定義的有:

  • ClassKey
  • IntKey
  • LongKey
  • StringKey

編譯后的代碼不貼了,跟set原理差不多

自定義key類型

enum MyEnum {
  ABC, DEF;
}

@MapKey
@interface MyEnumKey {
  MyEnum value();
}

@MapKey
@interface MyNumberClassKey {
  Class<? extends Number> value();
}

@Module
class MyModule {
  @Provides @IntoMap
  @MyEnumKey(MyEnum.ABC)
  static String provideABCValue() {
    return "value for ABC";
  }

  @Provides @IntoMap
  @MyNumberClassKey(BigDecimal.class)
  static String provideBigDecimalValue() {
    return "value for BigDecimal";
  }
}

這里定義了自定義兩種key:ABC,DEF

Map的高級(jí)用法請(qǐng)見(jiàn)下節(jié)壶谒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市膳沽,隨后出現(xiàn)的幾起案子汗菜,更是在濱河造成了極大的恐慌让禀,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陨界,死亡現(xiàn)場(chǎng)離奇詭異巡揍,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)菌瘪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)腮敌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人俏扩,你說(shuō)我怎么就攤上這事糜工。” “怎么了录淡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵捌木,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我嫉戚,道長(zhǎng)刨裆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任彬檀,我火速辦了婚禮帆啃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凤覆。我一直安慰自己链瓦,他們只是感情好拆魏,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布盯桦。 她就那樣靜靜地躺著,像睡著了一般渤刃。 火紅的嫁衣襯著肌膚如雪拥峦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天卖子,我揣著相機(jī)與錄音略号,去河邊找鬼。 笑死洋闽,一個(gè)胖子當(dāng)著我的面吹牛玄柠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诫舅,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼羽利,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了刊懈?” 一聲冷哼從身側(cè)響起这弧,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娃闲,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后匾浪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體皇帮,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蛋辈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了属拾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梯浪,死狀恐怖捌年,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挂洛,我是刑警寧澤礼预,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站虏劲,受9級(jí)特大地震影響托酸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柒巫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一励堡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堡掏,春花似錦应结、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至亭畜,卻和暖如春扮休,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拴鸵。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工玷坠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劲藐。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓八堡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親聘芜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子兄渺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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