Dagger 2學(xué)習(xí)與探索(八)

上一期介紹了Component dependency方法,這一期介紹SubComponent方法埋涧,效果是類似的闯睹,只是實(shí)現(xiàn)手段有一點(diǎn)不一樣剿干。
SubComponent的聲明不再是用@Component標(biāo)識(shí),而是使用@Subcomponent標(biāo)識(shí)怕磨,不再有dependency參數(shù)喂饥,同時(shí)上級(jí)Component也無需再添加暴露函數(shù)。
那么SubComponent是如何與上級(jí)建立聯(lián)系的呢肠鲫?
有兩種方式:

  • 在上級(jí)Component內(nèi)添加獲得該SubComponent的抽象方法员帮;
  • 從v2.7開始,允許在SubComponent內(nèi)添加builder导饲,使用build()方法來獲取實(shí)例捞高,同時(shí)還需要在上級(jí)Component內(nèi)添加SubComponentbinder的相關(guān)代碼,來實(shí)現(xiàn)映射渣锦。這種方法壞處是復(fù)雜很多硝岗,好處是解耦?以我目前看來泡挺,雖然不用寫工廠方法了辈讶,可還是要加更多新的東西到上級(jí),代價(jià)實(shí)在不對(duì)等娄猫。不過既然這個(gè)功能在這里贱除,還是了解一下比較好。

第一種方法

主體代碼

和上一期的非常類似媳溺,稍微做一下修改:
AppComponent不用再暴露ClassA的方法月幌,而加上ActivityComponent的構(gòu)造方法:

@PerApplication
@Component(modules = AppModule.class)
public interface AppComponent {

  void inject(MyApp myApp);
  
  ActivityComponent getActivityComponent(ActivityModule activityModule);
}

注意和提供/暴露函數(shù)不太一樣,這里雖然ActivityComponent自己有范圍標(biāo)注悬蔽,但在這里不必標(biāo)出扯躺。你也可以標(biāo)出,但不會(huì)造成任何區(qū)別蝎困。ActivityComponent不是注入對(duì)象录语,并不會(huì)變成單例模式。

@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {

  void inject(MainActivity mainActivity);
}

Module部分沒有變化禾乘。然后就是使用的時(shí)候有區(qū)別:

public class MainActivity extends AppCompatActivity {
  @Inject ClassB classB;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    MyApp.getAppComponent().getActivityComponent(new ActivityModule(4)).inject(this);
  }
}

現(xiàn)在ActivityComponent是由AppComponent產(chǎn)生澎埠。運(yùn)行之前還是要rebuild一下。
可以看到始藕,這種方法和上一期的難易程度相差不大蒲稳,不過由于Dagger1的時(shí)代就有類似Component dependency的模式氮趋,因此可能Component dependency用的更多一些。

生成代碼

還是來看看DaggerAppComponent吧〗現(xiàn)在DaggerActivityComponent已經(jīng)不存在了剩胁。

public final class DaggerAppComponent implements AppComponent {
  private Provider<Integer> provideIntAProvider;

  private Provider<Integer> provideIntBProvider;

  private Provider<ClassA> provideClassAProvider;

  private MembersInjector<MyApp> myAppMembersInjector;

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

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

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

    this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);

    this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);

    this.provideClassAProvider =
        DoubleCheck.provider(
            AppModule_ProvideClassAFactory.create(
                builder.appModule, provideIntAProvider, provideIntBProvider));

    this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);
  }

  @Override
  public void inject(MyApp myApp) {
    myAppMembersInjector.injectMembers(myApp);
  }

  @Override
  public ActivityComponent getActivityComponent(ActivityModule activityModule) {
    return new ActivityComponentImpl(activityModule);
  }

  public static final class Builder {
    private AppModule appModule;

    private Builder() {}

    public AppComponent build() {
      if (appModule == null) {
        throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerAppComponent(this);
    }

    public Builder appModule(AppModule appModule) {
      this.appModule = Preconditions.checkNotNull(appModule);
      return this;
    }
  }

  private final class ActivityComponentImpl implements ActivityComponent {
    private final ActivityModule activityModule;

    private Provider<Integer> provideCProvider;

    private Provider<ClassB> provideClassBProvider;

    private MembersInjector<MainActivity> mainActivityMembersInjector;

    private ActivityComponentImpl(ActivityModule activityModule) {
      this.activityModule = Preconditions.checkNotNull(activityModule);
      initialize();
    }

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

      this.provideCProvider = ActivityModule_ProvideCFactory.create(activityModule);

      this.provideClassBProvider =
          DoubleCheck.provider(
              ActivityModule_ProvideClassBFactory.create(
                  activityModule, DaggerAppComponent.this.provideClassAProvider, provideCProvider));

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

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

可見其內(nèi)部新生成了一個(gè)類,ActivityComponentImpl祥国,效果就像把上一期的DaggerActivityComponent合并到這里來昵观。

第二種方法

主體代碼
  1. ActivityComponent
@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {

  void inject(MainActivity mainActivity);

  @Subcomponent.Builder
  interface Builder extends SubcomponentBuilder<ActivityComponent> {
    Builder withActivityModule(ActivityModule activityModule);
  }
}

現(xiàn)在AppComponent沒有工廠方法了,ActivityComponent需要提供一個(gè)Builder來用參數(shù)構(gòu)造自己系宫。注意到@Subcomponent.Builder標(biāo)注索昂,看看究竟:

/**
 * A subcomponent that inherits the bindings from a parent {@link Component} or
 * {@link Subcomponent}. The details of how to associate a subcomponent with a parent are described
 * in the documentation for {@link Component}.
 *
 * @author Gregory Kick
 * @since 2.0
 */
@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
@Target(TYPE)
@Documented
public @interface Subcomponent {
  /**
   * A list of classes annotated with {@link Module} whose bindings are used to generate the
   * subcomponent implementation.  Note that through the use of {@link Module#includes} the full set
   * of modules used to implement the subcomponent may include more modules that just those listed
   * here.
   */
  Class<?>[] modules() default {};
  
  /**
   * A builder for a subcomponent.  This follows all the rules of {@link Component.Builder}, except
   * it must appear in classes annotated with {@link Subcomponent} instead of {@code Component}.
   * Components can have methods that return a {@link Subcomponent.Builder}-annotated type,
   * allowing the user to set modules on the subcomponent using their defined API.
   */
  @Target(TYPE)
  @Documented
  @interface Builder {}
}

正如注釋文檔描述的那樣,Builder接口需要有一個(gè)方法來幫助設(shè)置ActivityModule扩借,在這里就是withActivityModule方法椒惨,名字仍舊不重要。
SubcomponentBuilder是自己定義的一個(gè)接口潮罪,好在如果使用第二種方式康谆,這個(gè)接口可以復(fù)用在其他Subcomponent里:

public interface SubcomponentBuilder<V> {
  V build();
}

名字不重要,重要的是要帶有build()方法嫉到。這個(gè)接口與Builder配合來實(shí)現(xiàn)Builder.build()沃暗。

  1. AppComponent
@PerApplication
@Component(modules = {AppModule.class, ActivityBinders.class})
public interface AppComponent {

  void inject(MyApp myApp);

  Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders();
}

首先介紹一下ActivityBinders,這個(gè)負(fù)責(zé)獲取對(duì)應(yīng)的Builder

@Module(subcomponents={ ActivityComponent.class })
public abstract class ActivityBinders {

  @Binds
  @IntoMap
  @SubcomponentKey(Builder.class)
  public abstract SubcomponentBuilder getActivityBuilder(Builder impl);
}

注意其標(biāo)注何恶,屬于@Module而且要聲明對(duì)應(yīng)的Subcomponent孽锥。
出現(xiàn)了三個(gè)新標(biāo)注。
@Binds說是新標(biāo)注细层,其實(shí)在其他地方也見過惜辑。
@IntoMap

/**
 * The method's return type forms the type argument for the value of a {@code Map<K, Provider<V>>},
 * and the combination of the annotated key and the returned value is contributed to the map as a
 * key/value pair. The {@code Map<K, Provider<V>>} produced from the accumulation of values will be
 * immutable.
 *
 * @see <a >Map multibinding</a>
 */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface IntoMap {}

大意是標(biāo)注的方法會(huì)和某個(gè)Map<K, Provider<V>>建立聯(lián)系,準(zhǔn)確地說是返回類型和標(biāo)注類型會(huì)成為一個(gè)鍵值對(duì)疫赎。
返回類型這里自然是SubcomponentBuilder的實(shí)現(xiàn)類盛撑,標(biāo)注類型就是下面@SubcomponentKey(Builder.class)標(biāo)注的Builder.class,這個(gè)Builder就是ActivityComponent里面的Builder捧搞。
SubcomponentKey也是要自己添加的一個(gè)標(biāo)注接口抵卫,好在還是可以復(fù)用。

@MapKey
@Target({ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME)
public @interface SubcomponentKey {
  Class<?> value();
}

其實(shí)總的來說胎撇,現(xiàn)在AppComponent不直接產(chǎn)生ActivityComponent介粘,而是生成一個(gè)Map,key就是Builder的類(由@SubcomponentKey規(guī)定晚树,當(dāng)然你也可以直接用@MapKey碗短,不過具體實(shí)現(xiàn)有點(diǎn)不同),value自然就是對(duì)應(yīng)的BuilderProvider封裝的工廠题涨。ActivityComponent自己查表獲取Builder再傳入ActivityModule完成建造偎谁,從而再注入。
當(dāng)然了纲堵,這里使用了一些幫助的擴(kuò)展接口/標(biāo)注巡雨,從而使得添加多個(gè)Subcomoponent變得容易。不用也是完全可以的席函。

  1. MainActivity
    基于2的分析铐望,這里的代碼思路就很清晰了:
public class MainActivity extends AppCompatActivity {
  @Inject ClassB classB;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ActivityComponent.Builder builder
        = (ActivityComponent.Builder) MyApp.getAppComponent()
        .subcomponentBuilders()
        .get(ActivityComponent.Builder.class)
        .get();
    builder.withActivityModule(new ActivityModule(4)).build().inject(this);
  }
}
生成代碼

DaggerAppComponent

public final class DaggerAppComponent implements AppComponent {
  private Provider<Integer> provideIntAProvider;

  private Provider<Integer> provideIntBProvider;

  private Provider<ClassA> provideClassAProvider;

  private MembersInjector<MyApp> myAppMembersInjector;

  private Provider<ActivityComponent.Builder> activityComponentBuilderProvider;

  private Provider<SubcomponentBuilder> getActivityBuilderProvider;

  private Provider<Map<Class<?>, Provider<SubcomponentBuilder>>>
      mapOfClassOfAndProviderOfSubcomponentBuilderProvider;

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

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

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

    this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);

    this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);

    this.provideClassAProvider =
        DoubleCheck.provider(
            AppModule_ProvideClassAFactory.create(
                builder.appModule, provideIntAProvider, provideIntBProvider));

    this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);

    this.activityComponentBuilderProvider =
        new dagger.internal.Factory<ActivityComponent.Builder>() {
          @Override
          public ActivityComponent.Builder get() {
            return new ActivityComponentBuilder();
          }
        };

    this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;

    this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
        MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
            .put(ActivityComponent.Builder.class, getActivityBuilderProvider)
            .build();
  }

  @Override
  public void inject(MyApp myApp) {
    myAppMembersInjector.injectMembers(myApp);
  }

  @Override
  public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
    return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
  }

  public static final class Builder {
    private AppModule appModule;

    private Builder() {}

    public AppComponent build() {
      if (appModule == null) {
        throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerAppComponent(this);
    }

    public Builder appModule(AppModule appModule) {
      this.appModule = Preconditions.checkNotNull(appModule);
      return this;
    }
  }

  private final class ActivityComponentBuilder implements ActivityComponent.Builder {
    private ActivityModule activityModule;

    @Override
    public ActivityComponent build() {
      if (activityModule == null) {
        throw new IllegalStateException(ActivityModule.class.getCanonicalName() + " must be set");
      }
      return new ActivityComponentImpl(this);
    }

    @Override
    public ActivityComponentBuilder withActivityModule(ActivityModule activityModule) {
      this.activityModule = Preconditions.checkNotNull(activityModule);
      return this;
    }
  }

  private final class ActivityComponentImpl implements ActivityComponent {
    private Provider<Integer> provideCProvider;

    private Provider<ClassB> provideClassBProvider;

    private MembersInjector<MainActivity> mainActivityMembersInjector;

    private ActivityComponentImpl(ActivityComponentBuilder builder) {
      assert builder != null;
      initialize(builder);
    }

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

      this.provideCProvider = ActivityModule_ProvideCFactory.create(builder.activityModule);

      this.provideClassBProvider =
          DoubleCheck.provider(
              ActivityModule_ProvideClassBFactory.create(
                  builder.activityModule,
                  DaggerAppComponent.this.provideClassAProvider,
                  provideCProvider));

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

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

首先有些新的東西是下游的Builder的實(shí)現(xiàn)細(xì)節(jié),很典型的建造者模式茂附,這里就不過多分析了正蛙,主要是探究Map是怎么生成的。
注意到

  @Override
  public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
    return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
  }

這個(gè)Map同樣也被Provider接口封裝营曼,具體在

this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
        MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
            .put(ActivityComponent.Builder.class, getActivityBuilderProvider)
            .build();

這里進(jìn)行初始化乒验。MapProviderFactory實(shí)際上就是一個(gè)Map的工廠,把鍵值對(duì)放進(jìn)去蒂阱,然后使用get()來獲取Map:

/**
 * A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
 * {@code Map<K, Provider<V>>} when calling {@link #get} (as specified by {@link Factory}).
 細(xì)節(jié)省略……

那么.put(ActivityComponent.Builder.class, getActivityBuilderProvider)就是放鍵值對(duì)锻全,getActivityBuilderProvider又從哪里來?this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;
然后

    this.activityComponentBuilderProvider =
        new dagger.internal.Factory<ActivityComponent.Builder>() {
          @Override
          public ActivityComponent.Builder get() {
            return new ActivityComponentBuilder();
          }
        };

如此就把一個(gè)初始化好的ActivityComponentBuilder插入到了Map中录煤。
之后發(fā)生的就很自然了鳄厌。
總而言之,這種方法復(fù)雜度比第一種高得很明顯妈踊。少寫了一行ActivityComponent的工廠函數(shù)了嚎,代價(jià)是多了一個(gè)ActivityBinders以及配套的接口/注解,同時(shí)AppComponent還得添加其他東西廊营。至于好處那就見仁見智了歪泳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赘风,隨后出現(xiàn)的幾起案子夹囚,更是在濱河造成了極大的恐慌,老刑警劉巖邀窃,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荸哟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瞬捕,警方通過查閱死者的電腦和手機(jī)鞍历,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肪虎,“玉大人劣砍,你說我怎么就攤上這事∩染龋” “怎么了刑枝?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵香嗓,是天一觀的道長。 經(jīng)常有香客問我装畅,道長靠娱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任掠兄,我火速辦了婚禮像云,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚂夕。我一直安慰自己迅诬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布婿牍。 她就那樣靜靜地躺著侈贷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牍汹。 梳的紋絲不亂的頭發(fā)上铐维,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音慎菲,去河邊找鬼嫁蛇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛露该,可吹牛的內(nèi)容都是我干的睬棚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼解幼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼抑党!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起撵摆,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤底靠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后特铝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體暑中,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年鲫剿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鳄逾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灵莲,死狀恐怖雕凹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤枚抵,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布线欲,位于F島的核電站,受9級(jí)特大地震影響汽摹,放射性物質(zhì)發(fā)生泄漏询筏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一竖慧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逆屡,春花似錦圾旨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至莺治,卻和暖如春廓鞠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谣旁。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國打工床佳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人榄审。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓砌们,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搁进。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浪感,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

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