在這篇文章中你會看到什麼:java
@Scope
是什麼@Singleton
是什麼@Scope
和 @Component
如何協同做戰。Dagger2
的學習曲線確實是比較陡的,我認爲陡的點一是對 依賴注入(控制反轉)概念的理解,因此有了Dagger 2 系列(一) -- 前奏篇:依賴注入的基本介紹,另一個就是 對 Scope 的理解,對於此我也是翻看了大量的博客,大部分博客看完以後的感覺依舊是雲裏霧裏的,本身也是通過了學習、擱淺、再學習的往復過程,同時也看了一些國外的博客,對 Scope
的概念有了基本的認識。git
咱們首先看一下 froer_mcs 在 一文中談到 Scope 能給咱們帶來什麼github
In Dagger 2 scopes mechanism cares about keeping single instance of class as long as its scope exists. In practice it means that instances scoped in
@ApplicationScope
lives as long as Application object.@ActivityScope
keeps references as long as Activity exists (for example we can share single instance of any class between all fragments hosted in this Activity).apiIn short - scopes give us 「local singletons」 which live as long as scope itself.bash
Annotated dependencies are single-instances but related to component lifecycle (not the whole application).app
我的翻譯框架
Dagger2 中 Scope 機制保證在 Scope 的做用域內類會保持單例。在實際開發中這意味着在 @ApplicationScope 對應的做用域中類的實例對象的生命會像 Application 同樣長,在 @ActivityScope 的做用域內的類實例的生命週期和相應的 Activity 同樣長。(不要想固然的認爲 Dagger2 會根據 Scope 註解的字面意義實現相應的類實例的單例效果,實現這樣的效果是須要具體實現的。) 總的來講, Scope 機制會保證在 Scope 的生命週期內實現 "本地單例" 在 Component 的生命週期內,Scope 註解依賴會保證單例。(也就是說,此處的單例是 Component 生命週期內的單例,若是 Component 實例對象從新實例化的,則單例效果失效。)ide
經過以上的引用和翻譯不知道你是否從新認識了 Scope ,在上文中一個反覆強調的概念:post
在 Dagger2 中 Scope 機制能夠保證在 Scope 標記的 Component 做用域內 ,類會保持單例 。 (敲黑板,這句話很重要)學習
重申一遍:
**在 Dagger2 中 Scope 機制能夠保證在 Scope 標記的 Component 做用域內 ,類會保持單例 **
若是理解了這句話,那麼回過頭來看 @Singleton
這個註解,是否是有一種豁然開朗的感受。並非只有 @Singleton
註解標記的相關類生產的實例是單例的,是全部的 Scope(自定義 Scope)
標記的相關類生產的實例 都是單例 的,只不過這個單例是有條件的 -- 在 Scope 註解標記 Component 的做用域內生產的實例是單例的 。
Scope 機制下的單例其實和 @Singleton
的字面意義 沒有半毛錢關係,當初本身就是被這種錯誤的思想誤導了很長時間。其實若是你願意你,能夠把 @Singleton
換成任意單詞,什麼 @Dog
、@Cat
、@XXx
均可以,你只要保證這個註解標記的 Component 在 App 進程中爲單例的,而且獲得正確的實現(被正確的標記到 類構造器
或 Module 中的 @Provides
標記的方法),那麼它對應生成的類實例就是 單例 的。
@Singleton
之因此被默認實現,只是由於這可讓人根據它的字面意思,知道被他標記的相關生成的類實例爲單例,這符合了 Java 的命名規範。
上面談到的全都是理論,那麼咱們就是用相應的代碼來驗證他們。
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface AnyOneScope {
}
複製代碼
這裏爲了代表最後的 單例 和 Scope
的命名沒有任何關係,名字避免使用了容易給人形成疑惑的 ApplicationScope
、ActivityScope
等,而使用了 AnyOneScope
,可是其實這些名字都是無所謂的 。
public class AppleBean {
private String color;
private int weight;
public AppleBean() {
Log.e("TAG", "AppleBean");
}
}
複製代碼
public class OrgranBean {
private String color;
private int weight;
public OrgranBean() {
Log.e("TAG", "OrgranBean");
}
}
複製代碼
@Module
public class FruitModule {
@AnyOneScope
@Provides
AppleBean provideApple() {
return new AppleBean();
}
@AnyOneScope
@Provides
OrgranBean provideOrgran() {
return new OrgranBean();
}
}
複製代碼
該 Module
提供 AppleBean
、OrgranBean
實例對象的方法,兩個方法使用 @AnyOneScope 進行註解。
@AnyOneScope
@Component(modules = {FruitModule.class})
public interface FruitComponent {
void inject(FuriteScopeActivity mTestScopeActivity);
}
複製代碼
public class FuriteScopeActivity extends AppCompatActivity {
@Inject
AppleBean mAppleBeanA;
@Inject
AppleBean mAppleBeanB;
@Inject
OrgranBean mOrgranBeanA;
@Inject
OrgranBean mOrgranBeanB;
FruitComponent mFruitComponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_scope);
mFruitComponent = DaggerFruitComponent.builder().fruitModule(new FruitModule()).build();
mFruitComponent.inject(this);// 完成注入,沒有這句話是不行的
Log.e("TAG", "mFruitComponent1:" + mFruitComponent.toString());
Log.e("TAG", "mAppleBeanA:" + mAppleBeanA.toString());
Log.e("TAG", "mAppleBeanB:" + mAppleBeanB.toString());
Log.e("TAG", "mOrgranBeanA:" + mOrgranBeanA.toString());
Log.e("TAG", "mOrgranBeanB:" + mOrgranBeanB.toString());
}
}
複製代碼
如代碼所示,在完成注入後分別對 AppleBean
、OrgranBean
分別調用了兩次實例,按照上文中 咱們對 @Scope
的理解,那麼在這裏兩個類分別生成的類實例爲同一個,下面咱們運行代碼,查看日誌來驗證一下。
打印日誌以下:
E/TAG: mFruitComponent1:com.example.administrator.dagger2demo.practiceFifth.DaggerFruitComponent@7c6e469
mAppleBeanA:com.example.administrator.dagger2demo.practiceFifth.AppleBean@b7d6eee
mAppleBeanB:com.example.administrator.dagger2demo.practiceFifth.AppleBean@b7d6eee
mOrgranBeanA:com.example.administrator.dagger2demo.practiceFifth.OrgranBean@648ef8f
mOrgranBeanB:com.example.administrator.dagger2demo.practiceFifth.OrgranBean@648ef8f
複製代碼
能夠看到日誌分別打印了 FruitComponent 的實例 -- mFruitComponent
,AppleBean 的兩個實例 -- mAppleBeanA
、mAppleBeanB
,OrgranBean 的兩個實例 -- mOrgranBeanA
、mOrgranBeanB
,發現 mAppleBeanA
和 mAppleBeanB
的哈希值相同即爲 同一個對象、mOrgranBeanA
、mOrgranBeanB
的哈希值相同即爲 同一個對象,驗證了咱們對 @Scope
的認識 -- 實現單例 。
同時,爲了驗證 單例 是有做用域的 -- Component 的做用域內,在 FuriteScopeActivity
添加如下方法:
public void jump(View view) {
mFruitComponent = DaggerFruitComponent.builder().fruitModule(new FruitModule()).build();
mFruitComponent.inject(this);// 完成注入,沒有這句話是不行的
Log.e("TAG", "mFruitComponent2:" + mFruitComponent.toString());
Log.e("TAG", "mAppleBeanA:" + mAppleBeanA.toString());
Log.e("TAG", "mAppleBeanB:" + mAppleBeanB.toString());
Log.e("TAG", "mOrgranBeanA:" + mOrgranBeanA.toString());
Log.e("TAG", "mOrgranBeanB:" + mOrgranBeanB.toString());
}
複製代碼
從新運行程序,觀察打印日誌:
09-21 14:50:54.903 14184-14184/com.example.administrator.dagger2demo E/TAG: AppleBean
OrgranBean
mFruitComponent1:com.example.administrator.dagger2demo.practiceFifth.DaggerFruitComponent@7c6e469
mAppleBeanA:com.example.administrator.dagger2demo.practiceFifth.AppleBean@b7d6eee
mAppleBeanB:com.example.administrator.dagger2demo.practiceFifth.AppleBean@b7d6eee
mOrgranBeanA:com.example.administrator.dagger2demo.practiceFifth.OrgranBean@648ef8f
09-21 14:50:54.904 14184-14184/com.example.administrator.dagger2demo E/TAG: mOrgranBeanB:com.example.administrator.dagger2demo.practiceFifth.OrgranBean@648ef8f
複製代碼
生成的對象和哈希值的對應關係爲:
- DaggerFruitComponent --> 7c6e469
- AppleBean --> b7d6eee
- OrgranBean --> 648ef8f
09-21 14:53:37.624 14184-14184/com.example.administrator.dagger2demo E/TAG: AppleBean
OrgranBean
mFruitComponent2:com.example.administrator.dagger2demo.practiceFifth.DaggerFruitComponent@8196f9e
mAppleBeanA:com.example.administrator.dagger2demo.practiceFifth.AppleBean@50ada7f
mAppleBeanB:com.example.administrator.dagger2demo.practiceFifth.AppleBean@50ada7f
mOrgranBeanA:com.example.administrator.dagger2demo.practiceFifth.OrgranBean@16bea4c
mOrgranBeanB:com.example.administrator.dagger2demo.practiceFifth.OrgranBean@16bea4c
複製代碼
生成的對象和哈希值的對應關係爲:
- FruitComponent --> 8196f9e
- AppleBean --> 50ada7f
- OrgranBean --> 16bea4c
很明顯 AppleBean
、OrgranBean
從新生成了新的對象,難道不是單例了?難道上文的結論是錯誤的?其實不是這樣的,由於 FruitComponent
生成了新的對象,因此其做用域下的類從新生成了新的實例。 證實了:在 Scope 註解標記 Component 的做用域內生產的實例是單例的。
至此,對 Dagger2
中的 Scope
的理解就如上文所示了,我認爲內容比較容易內容理解,並且比較淺顯,不會增添你的疑惑,但願本身的這篇學習記錄能夠幫助到看到的全部同窗。
以上代碼你能夠在這裏看到 GitHub--Dagger2Demo