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)說明:
- 如果在
Module
的provideXXX
方法上加上了@Scope
聲明泄鹏,那么在與他關(guān)聯(lián)的Component
上也必須加上相同的@Scope
聲明
- 如果
Component
加上了@Scope
聲明,provideXXX
秧耗,那么和Component
不加聲明的情況相同备籽。 - 當(dāng)
Module
的provideXXX
方法和Component
都加上了@Scope
聲明,那么在Component
實(shí)例的生命周期內(nèi)分井,只會(huì)創(chuàng)建一個(gè)由provideXXX
方法返回的實(shí)例车猬。也就是說,該Component
會(huì)持有之前通過provideXXX
方法創(chuàng)建的實(shí)例的引用尺锚,如果之前創(chuàng)建過珠闰,那么就不再調(diào)用Module
的provideXXX
去創(chuàng)建新的實(shí)例,而是直接返回它之前持有的那一份瘫辩。
上面的例子中伏嗜,我們通過ScopeActivityModule
創(chuàng)建了兩種類型的數(shù)據(jù)坛悉,provideScopeActivityData()
方法上加上了@PerScopeActivity
,而提供ScopeActivityNormalData
的provideScopeActivityNormalData()
方法則沒有承绸,后面我們將會(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)部包含了ScopeActivitySharedData
和ScopeActivityNormalData
,前者在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)證秩铆。
在Activity
和Fragment
中,我們打印出變量的地址來驗(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ì)象添祸,因此在Activity
和Fragment
中地址相同滚粟。 -
ScopeActivitySharedData
:在它的provide
方法上,我們加上了@PerScopeActivity
注解刃泌,因此在Activity
和Fragment
中凡壤,它的地址相同。 -
ScopeActivityNormalData
:雖然在提供它的ScopeActivityModule
中加上了@PerScopeActivity
注解耙替,但是在provide
方法上沒有聲明亚侠,因此無論是在Activity
,還是在Fragment
中俗扇,都是指向不同的地址硝烂。 -
ScopeFragmentData
:用于演示如何通過繼承的方式,來實(shí)現(xiàn)依賴注入狐援。
更多文章钢坦,歡迎訪問我的 Android 知識(shí)梳理系列:
- Android 知識(shí)梳理目錄:http://www.reibang.com/p/fd82d18994ce
- 個(gè)人主頁:http://lizejun.cn
- 個(gè)人知識(shí)總結(jié)目錄:http://lizejun.cn/categories/