Dagger2 知識(shí)梳理(4) - @Scope 注解的使用

Dagger2 系列文章

Dagger2 知識(shí)梳理(1) - Dagger2 依賴注入的兩種方式
Dagger2 知識(shí)梳理(2) - @Qulifier 和 @Named 解決依賴注入迷失
Dagger2 知識(shí)梳理(3) - 使用 dependencies 和 @SubComponent 完成依賴注入
Dagger2 知識(shí)梳理(4) - @Scope 注解的使用


一、前言

對(duì)于@Scope注解胀屿,很多同學(xué)都疑惑匾荆,今天我們就來了解一下@Scope相關(guān)的知識(shí),這里將會(huì)分為兩部分介紹:

  • 單個(gè)Component情況下@Scope的作用
  • 組織多個(gè)Component情況下對(duì)于@Scope的限制

首先,我們需要了解@Scope其實(shí)是一個(gè) 元注解母怜,它和我們?cè)?Dagger2 知識(shí)梳理(2) - @Qulifier 和 @Named 解決依賴注入迷失 一文中介紹的@Qualifier一樣台丛,是用于 描述注解的注解,關(guān)于元注解更多的知識(shí)可以參考之前的這篇文章 Java&Android 基礎(chǔ)知識(shí)梳理(1) - 注解戴而。

@Scope所描述的注解用于兩個(gè)地方:

  • Component
  • Module中用于創(chuàng)建實(shí)例的provideXXX方法

而我們經(jīng)炒帐酰看見的@Singleton注解其實(shí)就是用@Scope描述的注解,雖然它的表面意思是“單例”所意,但是我們后面會(huì)看到它和單例其實(shí)并沒有必然的關(guān)系淮逊。

二、單個(gè) Component 情況下 @Scope 的使用

@Scope描述的注解類似于下面這樣扶踊,這里的PerScopeActivity就是用@Scope描述的注解:

@Documented
@Retention(RUNTIME)
@Scope
public @interface PerScopeActivity {}

有可能會(huì)用到該注解的有兩個(gè)地方:

  • Component
@Component(dependencies = {ScopeAppComponent.class}, modules = {ScopeActivityModule.class})
@PerScopeActivity
public interface ScopeActivityComponent {
    public void inject(ScopeActivity scopeActivity);
    ScopeFragmentComponent scopeFragmentComponent();
}
  • Module中用于創(chuàng)建實(shí)例的provideXXX方法
@Module
public class ScopeActivityModule {

    @Provides
    @PerScopeActivity
    public ScopeActivitySharedData provideScopeActivityData() {
        return new ScopeActivitySharedData();
    }

    @Provides
    public ScopeActivityNormalData provideScopeActivityNormalData() {
        return new ScopeActivityNormalData();
    }
}

在單個(gè)Component情況下使用@Scope有以下幾點(diǎn)說明:

  • 如果在ModuleprovideXXX方法上加上了@Scope聲明泄鹏,那么在與他關(guān)聯(lián)的Component上也必須加上相同的@Scope聲明
  • 如果Component加上了@Scope聲明,provideXXX秧耗,那么和Component不加聲明的情況相同备籽。
  • 當(dāng)ModuleprovideXXX方法和Component都加上了@Scope聲明,那么在Component實(shí)例的生命周期內(nèi)分井,只會(huì)創(chuàng)建一個(gè)由provideXXX方法返回的實(shí)例车猬。也就是說,該Component會(huì)持有之前通過provideXXX方法創(chuàng)建的實(shí)例的引用尺锚,如果之前創(chuàng)建過珠闰,那么就不再調(diào)用ModuleprovideXXX去創(chuàng)建新的實(shí)例,而是直接返回它之前持有的那一份瘫辩。

上面的例子中伏嗜,我們通過ScopeActivityModule創(chuàng)建了兩種類型的數(shù)據(jù)坛悉,provideScopeActivityData()方法上加上了@PerScopeActivity,而提供ScopeActivityNormalDataprovideScopeActivityNormalData()方法則沒有承绸,后面我們將會(huì)看到裸影,如果在目標(biāo)類中使用同一個(gè)ScopeActivityComponent注入,而有多個(gè)ScopeActivitySharedData變量的情況下它們指向的是同一塊內(nèi)存地址八酒,而ScopeActivityNormalData則會(huì)指向不同的內(nèi)存地址空民。

三、組織多個(gè) Component 情況下對(duì)于 @Scope 的限制

對(duì)于單個(gè)Component還比較好理解羞迷,但是在組織多個(gè)Component的情況下就有些復(fù)雜了界轩,這里的“組織”就是我們?cè)谇耙黄?Dagger2 知識(shí)梳理(3) - 使用 dependencies 和 @SubComponent 完成依賴注入 談到的 依賴方式繼承方式

  • 在依賴或者繼承的組織方式中衔瓮,如果其中一個(gè)Component聲明了@Scope浊猾,那么其它的Component也需要聲明。
  • 在依賴關(guān)系中热鞍,被依賴的Component和需要依賴的Component@Scope不能相同
  • 在依賴關(guān)系中葫慎,需要依賴的Component@Scope不可以為@Singleton
  • 在組織關(guān)系中薇宠,子Component@Scope不可以和父Component@Scope相同:
  • 在組織關(guān)系中偷办,如果父Component@Scope不為@Singleton,那么子Component@Scope可以為@Singleton澄港。

這些限制是由Dagger2在編譯時(shí)去檢查的椒涯,其目的是保證使用者不要對(duì)@Scope產(chǎn)生濫用的現(xiàn)象,因?yàn)?code>@Scope的目的是 在特定作用域內(nèi)控制被注入實(shí)例的復(fù)用回梧。

四废岂、示例

為了讓大家更好的驗(yàn)證上面關(guān)于@Scope的解釋,下面用一個(gè)Demo來演示狱意,完整代碼可以從 Dagger2Sample 的第四章獲取湖苞,這個(gè)Demo包括三個(gè)大部分:

(1) ScopeApp

對(duì)應(yīng)于我們平時(shí)的Application類,并提供了全局的ScopeAppData類详囤,在其ScopeAppComponent上有@Singleton注解财骨。

@Singleton
@Component(modules = {ScopeAppModule.class})
public interface ScopeAppComponent {
    public ScopeAppData getScopeAppData(); //如果它被其它的Component依賴,那么需要聲明getXXX方法纬纪。
}
@Module
public class ScopeAppModule {

    @Provides
    @Singleton
    public ScopeAppData provideScopeAppData() {
        return new ScopeAppData();
    }
}

(2) ScopeActivity

對(duì)應(yīng)于一個(gè)主頁面蚓再,其內(nèi)部包含了ScopeActivitySharedDataScopeActivityNormalData,前者在ScopeActivityComponent的生命周期內(nèi)保持唯一性包各,并帶有PerScopeActivity注解。

@Component(dependencies = {ScopeAppComponent.class}, modules = {ScopeActivityModule.class})
@PerScopeActivity
public interface ScopeActivityComponent {
    public void inject(ScopeActivity scopeActivity);
    ScopeFragmentComponent scopeFragmentComponent();
}
@Module
public class ScopeActivityModule {

    @Provides
    @PerScopeActivity
    public ScopeActivitySharedData provideScopeActivityData() {
        return new ScopeActivitySharedData();
    }

    @Provides
    public ScopeActivityNormalData provideScopeActivityNormalData() {
        return new ScopeActivityNormalData();
    }
}

(3) ScopeFragment

對(duì)于于Activity下的一個(gè)子界面靶庙,它和ScopeActivityComponent是繼承關(guān)系问畅,并帶有@PerScopeFragment注解:

@Subcomponent(modules = {ScopeFragmentModule.class})
@PerScopeFragment
public interface ScopeFragmentComponent {
    public void inject(ScopeFragment scopeFragment);
}
@Module
public class ScopeFragmentModule {

    @Provides
    @PerScopeFragment
    public ScopeFragmentData provideScopeFragmentData() {
        return new ScopeFragmentData();
    }
}

以上三個(gè)部分的關(guān)系為:

  • ScopeActivityComponent依賴于ScopeAppComponent
  • ScopeFragmentComponent繼承于ScopeActivityComponent
  • 它們的Module上都有用@Scope描述的注解:@Singleton@PerScopeActivity@PerScopeFragment护姆。

示例驗(yàn)證

通過這個(gè)例子可以覆蓋到上面我們介紹的所有場(chǎng)景矾端,大家可以直接在Github上查看,也可以clone下來卵皂,進(jìn)行修改驗(yàn)證秩铆。

ActivityFragment中,我們打印出變量的地址來驗(yàn)證前面的結(jié)論:

  • App
public class ScopeApp extends Application {

    private ScopeAppComponent mScopeAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mScopeAppComponent = DaggerScopeAppComponent.builder().scopeAppModule(new ScopeAppModule()).build();
    }

    public ScopeAppComponent getAppComponent() {
        return mScopeAppComponent;
    }
}
  • Activity
public class ScopeActivity extends AppCompatActivity {

    private static final String TAG = ScopeActivity.class.getSimpleName();

    private ScopeActivityComponent mScopeActivityComponent;

    @Inject
    ScopeAppData mScopeAppData;

    @Inject
    ScopeActivitySharedData mScopeActivitySharedData1;

    @Inject
    ScopeActivitySharedData mScopeActivitySharedData2;

    @Inject
    ScopeActivityNormalData mScopeActivityNormalData1;

    @Inject
    ScopeActivityNormalData mScopeActivityNormalData2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scope);
        getScopeActivityComponent().inject(this);
        TextView tvData = (TextView) findViewById(R.id.tv_scope_activity);
        String result = "[ScopeActivity Space] \n mScopeAppData=" + mScopeAppData
                + "\n\n" + "mScopeActivitySharedData1=" + mScopeActivitySharedData1
                + "\n\n" + "mScopeActivitySharedData2=" + mScopeActivitySharedData2
                + "\n\n" + "mScopeActivityNormalData1=" + mScopeActivityNormalData1
                + "\n\n" + "mScopeActivityNormalData2=" + mScopeActivityNormalData2;
        tvData.setText(result);
    }

    public ScopeActivityComponent getScopeActivityComponent() {
        if (mScopeActivityComponent == null) {
            ScopeAppComponent scopeAppComponent = ((ScopeApp) getApplication()).getAppComponent();
            mScopeActivityComponent = DaggerScopeActivityComponent.builder().scopeAppComponent(scopeAppComponent).build();
        }
        return mScopeActivityComponent;
    }
}
  • Fragment
public class ScopeFragment extends Fragment {

    private ScopeActivity mScopeActivity;

    @Inject
    ScopeAppData mScopeAppData;

    @Inject
    ScopeActivitySharedData mScopeActivitySharedData;

    @Inject
    ScopeActivityNormalData ScopeActivityNormalData;

    @Inject
    ScopeFragmentData mScopeFragmentData;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mScopeActivity = (ScopeActivity) context;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_scope, container, false);
        mScopeActivity.getScopeActivityComponent().scopeFragmentComponent().inject(this);
        TextView tv = (TextView) rootView.findViewById(R.id.tv_scope_fragment);
        String result = "[ScopeFragment Space] \n mScopeAppData=" + mScopeAppData
                + "\n\n" + "mScopeActivitySharedData1=" + mScopeActivitySharedData
                + "\n\n" + "ScopeActivityNormalData=" + ScopeActivityNormalData
                + "\n\n" + "mScopeFragmentData=" + mScopeFragmentData;
        tv.setText(result);
        return rootView;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

結(jié)果為:



由上面例子中的現(xiàn)象灯变,可以總結(jié)出以下幾點(diǎn):

  • ScopeAppData:該數(shù)據(jù)是由ScopeAppModule提供的殴玛,而它加上了@Singleton注解,并且我們調(diào)用的是同一個(gè)對(duì)象添祸,因此在ActivityFragment中地址相同滚粟。
  • ScopeActivitySharedData:在它的provide方法上,我們加上了@PerScopeActivity注解刃泌,因此在ActivityFragment中凡壤,它的地址相同。
  • ScopeActivityNormalData:雖然在提供它的ScopeActivityModule中加上了@PerScopeActivity注解耙替,但是在provide方法上沒有聲明亚侠,因此無論是在Activity,還是在Fragment中俗扇,都是指向不同的地址硝烂。
  • ScopeFragmentData:用于演示如何通過繼承的方式,來實(shí)現(xiàn)依賴注入狐援。

更多文章钢坦,歡迎訪問我的 Android 知識(shí)梳理系列:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市啥酱,隨后出現(xiàn)的幾起案子爹凹,更是在濱河造成了極大的恐慌,老刑警劉巖镶殷,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件禾酱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绘趋,警方通過查閱死者的電腦和手機(jī)颤陶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陷遮,“玉大人滓走,你說我怎么就攤上這事∶辈觯” “怎么了搅方?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵比吭,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我姨涡,道長(zhǎng)衩藤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任涛漂,我火速辦了婚禮赏表,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匈仗。我一直安慰自己瓢剿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布锚沸。 她就那樣靜靜地躺著跋选,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哗蜈。 梳的紋絲不亂的頭發(fā)上前标,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音距潘,去河邊找鬼炼列。 笑死,一個(gè)胖子當(dāng)著我的面吹牛音比,可吹牛的內(nèi)容都是我干的俭尖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼洞翩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼稽犁!你這毒婦竟也來了私爷?” 一聲冷哼從身側(cè)響起幔荒,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤羡宙,失蹤者是張志新(化名)和其女友劉穎堵漱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體篮条,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘟芝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年纲爸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俱笛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捆姜。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖迎膜,靈堂內(nèi)的尸體忽然破棺而出泥技,到底是詐尸還是另有隱情,我是刑警寧澤磕仅,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布零抬,位于F島的核電站镊讼,受9級(jí)特大地震影響宽涌,放射性物質(zhì)發(fā)生泄漏平夜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一卸亮、第九天 我趴在偏房一處隱蔽的房頂上張望忽妒。 院中可真熱鬧,春花似錦兼贸、人聲如沸段直。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸯檬。三九已至,卻和暖如春螺垢,著一層夾襖步出監(jiān)牢的瞬間喧务,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工枉圃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留功茴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓孽亲,卻偏偏與公主長(zhǎng)得像坎穿,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子返劲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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