dagger2 學習(三) - scope 使用

關于Scope

Dagger 2 自帶的 Scope 只有一個 @Singleton ,其他的可以通過自定義來實現

本文代碼

1. 前言

(1) Scope 的作用,就是提供在當前 Component 實例 范圍內的單例。

假設 DaggerUserComponent 能夠提供 User 實例

UserComponent 被自定義的 @UserScope 標注,那就意味著

一旦一個 DaggerUserComponent 實例創建完成,

那么其調用 injectTo 方法,進行注入時,所有注入的 User 對象都是同一個實例

知道 DaggerUserComponent 被重新創建,才會提供一個不一樣的User實例

(2) @Scope 的使用方法

第一種

  1. @Scope 注解整個 Bean 對象,@inject 注解對應 Bean 對象的構造方法
  2. @Scope 還需要在 Bean 對象注入,出現的 Component 中標注

第二種

  1. @Scope 配合 在Module 中使用,配合 @Provides 一起標注
  2. @Scope 需要在 Module 出現的 Component 中標注

兩種方法,其實就是兩種提供實例的不同實現,對比前面 一二兩篇文章即可看出

第一種是最簡單注入時,加上@Scope

第二種是配合@Module 注入式,加上@Scope


2. 進行實踐操作

(1) 整體結構構建

實踐的內容主要是針對 @Scope 第二種使用方法

因此這?中間@UserScope 只需要添加到 UserModuleUserComponent

具體代碼

整個類的結構

代碼結構
代碼結構

創建三個 Activity 分別用于顯示 User 實例

下面貼出部分代碼

自定義 UserScope.java

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}

User.java

public class User {
    ...//純 Bean 對象,無任何特殊
}

UserComponent.java

@UserScope// 綁定 UserScope
@Component(modules = {UserModule.class})
public interface UserComponent {
    void injectTo(ClassARoomActivity classARoomActivity);
    void injectTo(ClassBRoomActivity classBRoomActivity);
}

UserModule.java

@Module
public class UserModule {
    ...
    @UserScope// 綁定 UserScope
    @Provides
    User provideUser(){
        return new User();
    }
}

App.java

    ...
    static UserComponent sUserComponent;
    ...
    public static UserComponent getUserComponent(){// 獲取 DaggerUserComponent 對象
        if (sUserComponent == null){
            sUserComponent = DaggerUserComponent.builder().userModule(new UserModule())
                    .build();
        }
        return sUserComponent;
    }

    public static void releaseUserComponent(){ // 清空 DaggerUserComponent 對象
        sUserComponent = null;
    }
    ...

(2) 具體生成代碼和調用分析

a. 代碼生成部分分析

DaggerUserComponent.java 部分代碼變化

未加上 @UserScope 時,provideUserProvider 的生成

this.provideUserProvider = UserModule_ProvideUserFactory.create(builder.userModule);

加上 @UserScope 后,provideUserProvider 的生成

this.provideUserProvider =
    DoubleCheck.provider(UserModule_ProvideUserFactory.create(builder.userModule));

注意,雖然此處的 provideUserProvider 依然是 Provider<User>

但是,其實它的實例已經是 DoubleCheck<User> 類型的。

跟進這個DoubleCheck.provider() 方法

  public static <T> Provider<T> provider(Provider<T> delegate) {
    checkNotNull(delegate);
    if (delegate instanceof DoubleCheck) {
    // 如果是 DoubleCheck 的實例,直接返回
      return delegate;
    }
    // 否則創建一個,此處的 delegate 就是 UserModule_ProvideUserFactory.create(builder.userModule)
    return new DoubleCheck<T>(delegate);
  }

跟進構造方法

private DoubleCheck(Provider<T> provider) {
  assert provider != null;
  this.provider = provider;
  // 啥都沒有,就是賦值了一個 provider 引用
}

所以綜上,可以判斷出,和之前的沒有@UserScope 注解對比,具體的實例提供者改變了,不是生成UserModule_ProvideUserFactory 對象了,變成了DoubleCheck<User> 對象,其內部持有一個 UserModule_ProvideUserFactory 的引用。

b.整體調用鏈

DaggerUserComponent.injectTo -> ClassARoomActivity_MembersInjector.injectMembers() -> mUserProvider.get() -> DoubleCheck<User>.get()

下面進行具體分析

  • DaggerUserComponent.injectTo -> ClassARoomActivity_MembersInjector.injectMembers() 部分

    因此其調用的實例也有了對應的改變,對應的xxxInjector.javainjectMemebers 方法在調用時,會調用不同的實例

    該部分代碼和之前并無區別,主要是運行時,實例的區別

    @Override
    public void injectTo(ClassARoomActivity classARoomActivity) {
      classARoomActivityMembersInjector.injectMembers(classARoomActivity);
    }
    
    @Override
    public void injectMembers(ClassARoomActivity instance) {
      if (instance == null) {
        throw new NullPointerException("Cannot inject members into a null reference");
      }
      instance.mUser = mUserProvider.get();// 注意該部分會調用不同的實例對應的方法
    }
    
  • mUserProvider.get() -> DoubleCheck<User>.get()

    未加上@UserScop 時,實例是 UserModule_ProvideUserFactory

    調用的是UserModule_ProvideUserFactory.java 中的方法,如下

        @Override
        public User get() {
          return Preconditions.checkNotNull(
            module.provideUser(), "Cannot return null from a non-@Nullable @Provides method");
        }
    

    加上@UserScop 時,實例是DoubleCheck<User>

    調用的是 DoubleCheck<T> 中的方法,如下,該部分也是實現 Scope 功能重要的一部分

       public T get() {
                   Object result = instance;
                   if (result == UNINITIALIZED) {// 如果該對象從來沒有初始化,那就初始化一次
                     synchronized (this) {
                       result = instance;// 獲取最新實例,防止線程之間同時修改
                       if (result == UNINITIALIZED) {
                         result = provider.get();// 此處依舊調用了 UserModule_ProvideUserFactory.get() 方法
                         Object currentInstance = instance;
                         if (currentInstance != UNINITIALIZED && currentInstance != result) {
                           throw new IllegalStateException("Scoped provider was invoked recursively returning "
    + "different results: " + currentInstance + " & " + result);
               }
          instance = result;
          // 賦值最新的值
          provider = null;
          // 初始化一次以后,該對象對應的 Provider 在當前 Scope 中其實已經沒有意義了,
          // 所以直接置為空,方便 GC 回收
        }
      }
    }
    return (T) result;// 返回結果
    }
    
    
    >注意,Provider 的置空
    >
    >此處的置空不會影響數據的獲取,該 `provider` 的引用就是下面方法中的 `UserModule_ProvideUserFactory.create(builder.userModule)` 對
    >
    >```java
    >this.provideUserProvider =  DoubleCheck.provider(UserModule_ProvideUserFactory.create(builder.userModule));
    >```
    
    
    
    
    
    

3. 總結

總結:

@UserScope 作用于 Component 生命周期內

限制了被標注的實例提供者,只會實例化該對象一次,之后會拋棄對應的 Provider ,然后永遠獲取之前創建的User

@UserScope @Provides provideUser() ==> UserModule_ProviderUserFactory

此處拋棄的就是 UserModule_ProviderUserFactory 的實例

只有當實例化的 Component 對象被重新構建,被標注的實例提供者才會重新創建

Dagger2 學習(三)

一家之言,僅供參考

本文代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容