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阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容