Dagger2 系列文章
Dagger2 知識(shí)梳理(1) - Dagger2 依賴注入的兩種方式
Dagger2 知識(shí)梳理(2) - @Qulifier 和 @Named 解決依賴注入迷失
Dagger2 知識(shí)梳理(3) - 使用 dependencies 和 @SubComponent 完成依賴注入
Dagger2 知識(shí)梳理(4) - @Scope 注解的使用
一、前言
對(duì)于@Scope
注解,很多同學(xué)都疑惑,今天我們就來(lái)了解一下@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)常看見(jiàn)的@Singleton
注解其實(shí)就是用@Scope
描述的注解,雖然它的表面意思是“單例”,但是我們后面會(huì)看到它和單例其實(shí)并沒(méi)有必然的關(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)說(shuō)明:
- 如果在
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í)例。也就是說(shuō),該Component
會(huì)持有之前通過(guò)provideXXX
方法創(chuàng)建的實(shí)例的引用,如果之前創(chuàng)建過(guò),那么就不再調(diào)用Module
的provideXXX
去創(chuàng)建新的實(shí)例,而是直接返回它之前持有的那一份。
上面的例子中,我們通過(guò)ScopeActivityModule
創(chuàng)建了兩種類型的數(shù)據(jù),provideScopeActivityData()
方法上加上了@PerScopeActivity
,而提供ScopeActivityNormalData
的provideScopeActivityNormalData()
方法則沒(méi)有,后面我們將會(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
來(lái)演示,完整代碼可以從 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è)主頁(yè)面,其內(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)證
通過(guò)這個(gè)例子可以覆蓋到上面我們介紹的所有場(chǎng)景,大家可以直接在Github
上查看,也可以clone
下來(lái),進(jìn)行修改驗(yàn)證。
在Activity
和Fragment
中,我們打印出變量的地址來(lái)驗(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
方法上沒(méi)有聲明,因此無(wú)論是在Activity
,還是在Fragment
中,都是指向不同的地址。 -
ScopeFragmentData
:用于演示如何通過(guò)繼承的方式,來(lái)實(shí)現(xiàn)依賴注入。
更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:
- Android 知識(shí)梳理目錄:http://www.lxweimin.com/p/fd82d18994ce
- 個(gè)人主頁(yè):http://lizejun.cn
- 個(gè)人知識(shí)總結(jié)目錄:http://lizejun.cn/categories/