理解@Scope

關於Dagger2使用的基礎如果你還不瞭解,可以參考我的上一篇文章解鎖Dagger2使用姿勢(一),這有助於你理解本篇文章。


OK,我們在上篇文章介紹另外Dagger2使用過程中四個基本的註解,分別是@Module、@Provides、@Inject以及@Component。今天我想來說說Dagger2中另外一個註解,那就是@Scope註解。看網上的資料,大家基本上都知道@Scope是用來給依賴劃定作用域的,那我今天就來來說說這個作用域的問題。

首先Dagger2中有一個現成的作用域註解,那就是@Singleton,其實這個@Singleton還是由Java提供的。那麼這個註解的作用域就是讓我們的依賴成爲單例模式。比如下面一個例子:假設我有一個用戶對象,如下:

[java] view plain copy
 print?
  1. public class User {  
  2.   
  3.     ....  
  4.     ....  
  5.       
  6.     @Inject  
  7.     public User() {  
  8.     }  
  9.   
  10.     ....  
  11.     ....  
  12.       
  13. }  

我這裏暫時先用@Inject來註解這個用戶對象,避免使用Module,然後我需要一個Component來注入這個User對象到我的Activity中,我的Component如下:

[java] view plain copy
 print?
  1. @Component  
  2. public interface ActivityComponent {  
  3.     void inject(MainActivity activity);  
  4. }  

OK,然後我在我的MainActivity中初始化User對象,並將其地址顯示出來:

[java] view plain copy
 print?
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     @Inject  
  4.     User user;  
  5.     @Inject  
  6.     User user2;  
  7.     private TextView tv;  
  8.     private TextView tv2;  
  9.   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.         DaggerActivityComponent.builder().build().inject(this);  
  15.         tv = ((TextView) findViewById(R.id.tv));  
  16.         tv2 = ((TextView) findViewById(R.id.tv2));  
  17.         tv.setText(user.toString());  
  18.         tv2.setText(user2.toString());  
  19.     }  
  20. }  

顯示結果如下:

OK ,大家看到這完全是兩個不同的對象,兩個對象的引用地址完全不一樣。那如果我想讓User變爲一個單例模式該怎麼辦呢?其實很簡單,添加上@Singleton註解就可以了,在哪裏添加呢?兩個地方:

1.給User類添加上@Singleton註解:

[java] view plain copy
 print?
  1. @Singleton  
  2. public class User {  
  3.   
  4.     ....  
  5.     ....  
  6.       
  7.     @Inject  
  8.     public User() {  
  9.     }  
  10.   
  11.     ....  
  12.     ....  
  13.       
  14. }  

2.給ActivityComponent添加上@Singleton註解

[java] view plain copy
 print?
  1. @Singleton  
  2. @Component  
  3. public interface ActivityComponent {  
  4.     void inject(MainActivity activity);  
  5. }  

如此之後我再運行,結果如下:

OK,這個時候User對象就會以單例形式存在於我的App中了。OK,那如果我的項目中有Module,又該如何使我的User對象單例呢?

很簡單,在Module中提供User對象,提供User對象的方法需要有單例註解:

[java] view plain copy
 print?
  1. @Module  
  2. public class UserModule {  
  3.     @Provides  
  4.     @Singleton  
  5.     User providesUser() {  
  6.         return new User();  
  7.     }  
  8. }  

這個時候User對象上的註解就都可以去掉了,然後稍微修改一下ActivityComponent,如下:

[java] view plain copy
 print?
  1. @Singleton  
  2. @Component(modules = UserModule.class)  
  3. public interface ActivityComponent {  
  4.     void inject(MainActivity activity);  
  5. }  

然後MainActivity中初始化的代碼也要稍微修改一下下:

[java] view plain copy
 print?
  1. DaggerActivityComponent.builder().userModule(new UserModule()).build().inject(this);  

然後再運行,依然是單例模式。

OK,那我們知道@Singleton註解實際上實現的是一個全局單例模式,在實際開發中我們可能還需要一種局部單例的控件(這個應該是更常用),比如說我們有三個Activity,MainActivity,BActivity和CActivity,我們想讓MainActivity和BActivity共享同一個實例,而讓CActivity獲取另外一個實例,這又該怎麼實現呢?在Dagger2中,我們可以通過自定義Scope來實現局部單例。爽歪歪吧!OK,那就動手吧:

首先讓我們先來定義一個局部作用域:

[java] view plain copy
 print?
  1. @Scope  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. public @interface UserScope {  
  4. }  

然後在我們的UserModule和ActivityComponent中應用該局部作用域:

[java] view plain copy
 print?
  1. @Module  
  2. public class UserModule {  
  3.     @Provides  
  4.     @UserScope  
  5.     User providesUser() {  
  6.         return new User();  
  7.     }  
  8. }  

[java] view plain copy
 print?
  1. @UserScope  
  2. @Component(modules = UserModule.class)  
  3. public interface ActivityComponent {  
  4.     void inject(MainActivity activity);  
  5.   
  6.     void inject(BActivity activity);  
  7. }  

請大家注意,我的ActivityComponent作爲一個注入器只可以向MainActivity和BActivity兩個Activity中注入依賴,不可以向CActivity中注入依賴。最後,要讓該局部作用域產生單例效果,需要我們在自定義的Appliation類中來初始化這個Component,如下:

[java] view plain copy
 print?
  1. public class MyApp extends Application {  
  2.     ActivityComponent activityComponent;  
  3.     @Override  
  4.     public void onCreate() {  
  5.         super.onCreate();  
  6.         activityComponent = DaggerActivityComponent.builder().userModule(new UserModule()).build();  
  7.     }  
  8.   
  9.     ActivityComponent getActivityComponent(){  
  10.         return activityComponent;  
  11.     }  
  12. }  
接下來我們在MainActivity和BActivity中注入依賴,MainActivity如下:

[java] view plain copy
 print?
  1.     @Inject  
  2.     User user;  
  3.     @Inject  
  4.     User user2;  
  5.     private TextView tv;  
  6.     private TextView tv2;  
  7.   
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12. //        DaggerActivityComponent.builder().userModule(new UserModule()).build().inject(this);  
  13.         ((MyApp) getApplication()).getActivityComponent().inject(this);  
  14.         tv = ((TextView) findViewById(R.id.tv));  
  15.         tv2 = ((TextView) findViewById(R.id.tv2));  
  16.         tv.setText(user.toString());  
  17.         tv2.setText(user2.toString());  
  18.     }  

BActivity如下:

[java] view plain copy
 print?
  1. @Inject  
  2. User user;  
  3.   
  4. @Override  
  5. protected void onCreate(Bundle savedInstanceState) {  
  6.     super.onCreate(savedInstanceState);  
  7.     setContentView(R.layout.activity_b);  
  8.     ((MyApp) getApplication()).getActivityComponent().inject(this);  
  9.     TextView tv = (TextView) findViewById(R.id.tv);  
  10.     tv.setText(user.toString());  
  11. }  

那麼如果我還想在CActivity中使用User對象該怎麼辦呢?再來一個CUserModule和CActivityComponent唄!

CUserModule如下:

[java] view plain copy
 print?
  1. @Module  
  2. public class CUserModule {  
  3.     @Provides  
  4.     User providesUser() {  
  5.         return new User();  
  6.     }  
  7. }  

這裏我沒有再註明單例了哦!

CActivityComponent如下:

[java] view plain copy
 print?
  1. @Component(modules = CUserModule.class)  
  2. public interface CActivityComponent {  
  3.     void inject(CActivity activity);  
  4. }  

在CActivity中注入依賴:

[java] view plain copy
 print?
  1. @Inject  
  2. User user;  
  3. @Override  
  4. protected void onCreate(Bundle savedInstanceState) {  
  5.     super.onCreate(savedInstanceState);  
  6.     setContentView(R.layout.activity_c);  
  7.     DaggerCActivityComponent.builder().cUserModule(new CUserModule()).build().inject(this);  
  8.     TextView tv = (TextView) findViewById(R.id.tv);  
  9.     tv.setText(user.toString());  
  10. }  

運行結果如下:


大家看到,MainActivity和BActivity是同一個實例,而CActivity則是另外一個實例。

那麼可能會有小夥伴問@UserScope在這裏到底起了什麼作用?我引用網上一段話:

以下文字來源:http://blog.csdn.net/u012943767/article/details/51941872


[java] view plain copy
 print?
  1. Scope的使用,如何實現單例?  
  2.   
  3. 這個迷之Scope也是有點難以理解,我們在哪裏使用到了Scope呢。在我們的AppComponent中添加了一個註解爲@Singleton@Singleton就是一個Scope,據說可以實現單例喲。。。難道這樣就實現了單例模式?我剛剛開始是這樣理解的。直到仔細的看了幾遍這篇文章我才知道並不是這樣的。  
  4.   
  5. 事實上@Sinleton中並沒有創建單例的能力,那麼AppComponent中提供的依賴注入是如何實現單例的呢。其實這個原理很賤單。  
  6.   
  7. 首先Module提供了創建實例的方法,接着AppComponent中對Module進行管理,最後AppComponent在自定義Applicaiton中被實例化了一次。  
  8.   
  9. 這個實例化了一次是最重要的呀。僅僅被實例化了一次,那不就是單例麼。就是這麼簡單呀。  
  10.   
  11. 可能有些童靴當時就不樂意了,那既然這樣都已經實現了單例,那麼這個@Singltop還要來何用?不是多此一舉嗎。  
  12.   
  13. 其實@Singletop還有有一些作用的,首先一方面能讓你直面的瞭解到這是一個單例,其次這個@Singletop能夠更好的管理Modlue和Component之間的關係。  
  14.   
  15. Dagger2需要保證Component和Module是匹配的,就需要用到這個註解。  



OK,這就是Dagger2中@Scope註解的使用。後面我再來介紹Component之間的依賴關係。


以上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章