Dagger 2學習與探索(七)

上一期介紹了@Scope的含義和用法符衔,明白了Dagger2只認這么一個標注残炮,而且認為標注的Component為單例韭赘。那么假如我們想要實現(xiàn)真正的@PerActivity,即Activity范圍內(nèi)為單例势就,或者是其余的自定義范圍標注泉瞻,要如何做到呢?

范圍標注也沒有魔法苞冯,換言之不是標了@PerActivity它就自動實現(xiàn)Activity內(nèi)單例袖牙。更確切地講,是因為我們希望Activity內(nèi)單例舅锄,而且Dagger也會幫我們實現(xiàn)Activity內(nèi)單例鞭达,才給它標注@PerActivity

這里就不得不提Component dependencySubcomponent。它們是不同的東西畴蹭,但功能非常類似坦仍,都能使Component“層次化”∵督螅“層次化”的含義是繁扎,Component出現(xiàn)上下級之分,下級Component依賴于上級Component的參數(shù)與提供函數(shù)糊闽,然而又可以使用不同的范圍標注梳玫。(事實上,Dagger要求下級Component必須使用不同的范圍標注右犹,道理也很簡單提澎,相同范圍內(nèi)上級Component功能完全被下級覆蓋,沒有存在的必要性念链。

由于篇幅所限盼忌,本期只介紹Component dependency方法。

所謂Component dependency就是在上級Component內(nèi)創(chuàng)建提供函數(shù)掂墓,讓下級Component通過標注的形式來依賴上級Component(如果還有下下級碴犬,需要上級的參數(shù),還可繼承上級的接口Component梆暮,這樣下下級也可以訪問到上級的提供函數(shù))服协。

如此一來,我們可以讓Activity擁有自己的專屬ComponentActivityComponent啦粹,然后每次onCreate的時候從上級AppComponent里面造一個下級ActivityComponent偿荷,這樣還是只需要在初始化上級``AppComponent時提供一次構(gòu)造參數(shù)(當然如果下級ActivityComponent加入了新的Module的話還是要提供新參數(shù)),同時又因為下級ActivityComponent是獨立的唠椭,如果只在此Activity里面使用跳纳,就真正實現(xiàn)了@PerActivity`。

接下來我們將:創(chuàng)建AppComponent贪嫂,@PerApplication標注寺庄,負責注入ClassA
創(chuàng)建ActivityComponent力崇,@PerActivity標注斗塘,負責注入ClassBActivityComponent依賴于AppComponent亮靴。

共同代碼

無論如何馍盟,先定義好兩個范圍標注:

@Scope
@Documented
@Retention(RUNTIME)
public @interface PerApplication {}
@Scope
@Documented
@Retention(RUNTIME)
public @interface PerActivity {}

然后是ClassAClassB的代碼,都在構(gòu)造器內(nèi)添加Log以幫助了解構(gòu)造器的調(diào)用:

public class ClassA {

  private int a;
  private int b;
  private static final String TAG = "ClassA";

  public ClassA(int a, int b) {
    this.a = a;
    this.b = b;
    Log.d(TAG, "classA constructor called");
  }

  public int getA() {
    return a;
  }

  public int getB() {
    return b;
  }
}
public class ClassB {
  private static final String TAG = "ClassB";
  private ClassA classA;
  private int a;

  public ClassB(ClassA classA, int a) {
    this.classA = classA;
    this.a = a;
    Log.d(TAG, "classB constructor called");
  }

  public ClassA getClassA() {
    return classA;
  }

  public int getA() {
    return a;
  }
}

再把之前關(guān)于ClassBComponentModuleB的代碼刪掉茧吊。注意運行前rebuild一下贞岭,避免之前生成的代碼造成影響八毯。

Component Dependency

主體代碼

使用這種方式,上級Component可以暴露自己的組件給下級Component瞄桨,方法就是創(chuàng)建一個返回想要暴露的類型的函數(shù)话速,名字還是無所謂。比如這里的AppComponent是這樣:

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

  void inject(MyApp myApp);

  ClassA getClassA();
}

對應(yīng)的AppModule

@Module
public class AppModule {
  private int a;
  private int b;

  public AppModule(int a, int b) {
    this.a = a;
    this.b = b;
  }

  @Provides
  @Named("a")
  int provideIntA() {
    return a;
  }

  @Provides
  @Named("b")
  int provideIntB() {
    return b;
  }

  @PerApplication
  @Provides
  ClassA provideClassA(@Named("a") int a, @Named("b") int b) {
    return new ClassA(a, b);
  }
}

注意在AppComponent里面我們通過加入ClassA getClassA();暴露出ClassA@Provides函數(shù)給其下級芯侥,這樣下級就可以從上級獲取ClassA而不用自己再寫一遍尿孔。
然后就是ActivityComponent

@PerActivity
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {

  void inject(MainActivity mainActivity);
}

注意這種聲明依賴的形式。
有時候我們會看到有些項目中筹麸,下級Component不僅在標注上聲明了dependencies,還繼承了上級Component的接口雏婶。其實接口繼承就是單純地繼承上級中的暴露函數(shù)物赶,往往這個Component還有下級。
如果是最下級的Component留晚,并沒有理由這么做酵紫。
然后是ActivityModule

@Module
public class ActivityModule {

  private int c;

  public ActivityModule(int c) {
    this.c = c;
  }

  @Provides
  public int provideC() {
    return this.c;
  }

  @PerActivity
  @Provides
  public ClassB provideClassB(ClassA classA, int c) {
    return new ClassB(classA, c);
  }
}

然后就是MyApp代碼:

public class MyApp extends Application {

  private static MyApp appInstance = null;
  private static AppComponent appComponent = null;

  @Inject ClassA classa;

  @Override
  public void onCreate() {
    super.onCreate();

    appInstance = this;
    appComponent = DaggerAppComponent.builder().appModule(new AppModule(2, 3)).build();
    appComponent.inject(this);
  }

  public static MyApp getAppInstance() {
    return appInstance;
  }

  public static AppComponent getAppComponent() {
    return appComponent;
  }
}

MainActivity代碼:

public class MainActivity extends AppCompatActivity {
  @Inject ClassB classB;

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

    DaggerActivityComponent.builder()
        .activityModule(new ActivityModule(4))
        .appComponent(MyApp.getAppComponent())
        .build().inject(this);
  }
}

重復一遍:刪除之前ClassBComponentModuleB的代碼,rebuild再運行错维。運行發(fā)現(xiàn)兩個Log分別被打印一次奖地。這說明了A類和B類都只實例化了一次,也就是說B類的注入確實依賴了之前注入的A類赋焕。

生成代碼

現(xiàn)在有5個工廠類参歹,AppModule_ProvideIntAFactoryAppModule_ProvideIntBFactory隆判,AppModule_ProvideClassAFactory犬庇,ActivityModule_ProvideCFactoryActivityModule_ProvideClassBFactory
兩個注入器MyApp_MembersInjectorMainActivity_MembersInjector侨嘀。這些代碼都和之前的非常雷同臭挽。
我們還是把更多精力投入在兩個關(guān)鍵類DaggerAppComponentDaggerActivityComponent

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 ClassA getClassA() {
    return provideClassAProvider.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;
    }
  }
}

這個類就本身而言和上一期的差距不是很大,可以看出還是用了DoubleCheck封裝咬腕,說明確實Dagger把@PerApplication也看做單例欢峰。
唯一的區(qū)別在于,多了getClassA()這個函數(shù)涨共。這是自然的纽帖,因為這個類說到底是我們定義的AppComponent接口的實現(xiàn)類,自然要實現(xiàn)里面的抽象方法举反。
接下來這個就更值得仔細研究了:

public final class DaggerActivityComponent implements ActivityComponent {
  private Provider<ClassA> getClassAProvider;

  private Provider<Integer> provideCProvider;

  private Provider<ClassB> provideClassBProvider;

  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

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

    this.getClassAProvider =
        new daggerplay_com_daggerplay_components_AppComponent_getClassA(builder.appComponent);

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

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

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

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

  public static final class Builder {
    private ActivityModule activityModule;

    private AppComponent appComponent;

    private Builder() {}

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

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

    public Builder appComponent(AppComponent appComponent) {
      this.appComponent = Preconditions.checkNotNull(appComponent);
      return this;
    }
  }

  private static class daggerplay_com_daggerplay_components_AppComponent_getClassA
      implements Provider<ClassA> {
    private final AppComponent appComponent;

    daggerplay_com_daggerplay_components_AppComponent_getClassA(AppComponent appComponent) {
      this.appComponent = appComponent;
    }

    @Override
    public ClassA get() {
      return Preconditions.checkNotNull(
          appComponent.getClassA(), "Cannot return null from a non-@Nullable component method");
    }
  }
}

這個類創(chuàng)造了一個新的內(nèi)部類抛计,這里是daggerplay_com_daggerplay_components_AppComponent_getClassA。名字倒是其次照筑,其作用就是接受AppComponent的實例吹截,然后封裝成為ClassA的工廠瘦陈。我們在MainActivity里面?zhèn)魅氲?code>AppComponent的實例正是被如此轉(zhuǎn)化成為了依賴工廠來提供ClassA實例。另外由于AppComponent也使用了@PerActivity標注波俄,注入時還是使用上一期介紹的單例注入方法晨逝,有興趣的同學可以自己動手嘗試。

至此懦铺,我們明白了如果根據(jù)環(huán)境的結(jié)構(gòu)層次來對Dagger2進行分級以及拓展捉貌,不用擔心AppComponent里面注入太多Application層并用不到的東西,也不用擔心在寫Activity的時候還要把Application里面的東西都再重復一遍了冬念,是不是很有用呢趁窃?

下期繼續(xù)介紹Subcomponent的使用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末急前,一起剝皮案震驚了整個濱河市醒陆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌裆针,老刑警劉巖刨摩,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異世吨,居然都是意外死亡澡刹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門耘婚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罢浇,“玉大人,你說我怎么就攤上這事沐祷〖狠海” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵戈轿,是天一觀的道長凌受。 經(jīng)常有香客問我,道長思杯,這世上最難降的妖魔是什么胜蛉? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮色乾,結(jié)果婚禮上誊册,老公的妹妹穿的比我還像新娘。我一直安慰自己暖璧,他們只是感情好案怯,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著澎办,像睡著了一般嘲碱。 火紅的嫁衣襯著肌膚如雪金砍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天麦锯,我揣著相機與錄音恕稠,去河邊找鬼。 笑死扶欣,一個胖子當著我的面吹牛鹅巍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播料祠,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼骆捧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了髓绽?” 一聲冷哼從身側(cè)響起敛苇,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎梧宫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摆碉,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡塘匣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了巷帝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忌卤。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖楞泼,靈堂內(nèi)的尸體忽然破棺而出驰徊,到底是詐尸還是另有隱情,我是刑警寧澤堕阔,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布棍厂,位于F島的核電站,受9級特大地震影響超陆,放射性物質(zhì)發(fā)生泄漏牺弹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一时呀、第九天 我趴在偏房一處隱蔽的房頂上張望张漂。 院中可真熱鬧,春花似錦谨娜、人聲如沸航攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漠畜。三九已至币他,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盆驹,已是汗流浹背圆丹。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躯喇,地道東北人辫封。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像廉丽,于是被迫代替她去往敵國和親倦微。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

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

  • 什么是Dagger2 1.Dagger2是一個依賴注入框架正压。如果Class A 內(nèi)部 有一個Class B欣福,我們稱...
    天天大保建閱讀 962評論 0 1
  • 寫在前面:我目前就職于阿里巴巴-菜鳥,團隊目前缺人焦履,招聘java和客戶端開發(fā)拓劝,招聘對象為:社招和19屆畢業(yè)的校招生...
    littleKang閱讀 115,391評論 93 745
  • 自從 Dagger2 出現(xiàn),個人對 Dagger2 的學習過程也是斷斷續(xù)續(xù)的嘉裤,一直沒有系統(tǒng)的總結(jié)過郑临,所以也談不上掌...
    DthFish閱讀 1,084評論 0 14
  • 元宵夜,走在城市的街巷屑宠,到處張燈結(jié)彩厢洞,火樹銀花,人們老少相攜典奉,男女牽手躺翻,冒著料峭春寒,觀景賞月卫玖,但這明如白晝...
    姜楠竹賢閱讀 760評論 0 0
  • 前天講李白灶壶,昨晚老大說讓媽媽講杜甫的故事竿拆,書上怎么寫我就怎么念,杜甫的《相思》是鄰居小哥哥教會的笨触,很建議小伙伴們周...
    小祥子閱讀吧閱讀 107評論 0 0