Dagger 2 系列(五)——进阶之@Scope 和 @Singleton

前言:

在上一篇Dagger 2 系列(四)——@Named 和 @Qualifier注解介绍,了@Named 和 @Qualifier注解,这篇文章,我们将会了解另外俩个注解:@Scope 和 @Singleton。

在这篇文章中你会了解到:

  1. @Scope 是什么
  2. @Singleton 是什么
  3. @Scope和 @Component 如何协同作战。

Dagger2 的学习曲线确实是比较陡的,个人认为陡的点一是对依赖注入(控制反转)概念的理解,所以有了Dagger 2 系列(一)——基础篇之依赖注入的介绍,另外一个就是 对 Scope 的理解,对于此我也是翻看了大量的博客,经过了反复学习、再学习的往复过程,才对 Scope 的概念有了基本的认识。

1. @Scope

我们首先看一下 froer_mcs 在 一文中谈到 Scope 能给我们带来什么
在这里插入图片描述
翻译过来就是:

Dagger2 中 Scope 机制保证在 Scope 的作用域内类会保持单例。在实际开发中这意味着在 @ApplicationScope 对应的作用域中类的实例对象的生命会像 Application 一样长,在 @ActivityScope 的作用域内的类实例的生命周期和相应的 Activity 一样长。(不要想当然的认为 Dagger2 会根据 Scope 注解的字面意义实现相应的类实例的单例效果,实现这样的效果是需要具体实现的。)
总的来说, Scope 机制会保证在 Scope 的生命周期内实现 “本地单例”
在 Component 的生命周期内,Scope 注解依赖会保证单例。(也就是说,此处的单例是 Component 生命周期内的单例,如果 Component 实例对象重新实例化的,则单例效果失效。)

通过以上的引用和翻译不知道你是否重新认识了 Scope ,在上文中一个反复强调的概念:

在 Dagger2 中 Scope 机制可以保证在 Scope 标记的 Component 作用域内 ,类会保持单例 。

2. @Singleton

重申一遍:

在 Dagger2 中 Scope 机制可以保证在 Scope 标记的 Component 作用域内 ,类会保持单例 。

如果理解了这句话,那么回过头来看 @Singleton 这个注解,是不是有一种豁然开朗的感觉。并不是只有 @Singleton 注解标记的相关类生产的实例是单例的,是所有的 Scope(自定义 Scope) 标记的相关类生产的实例 都是单例 的,只不过这个单例是有条件的 – 在 Scope 注解标记 Component 的作用域内生产的实例是单例的 。
Scope 机制下的单例其实和 @Singleton 的字面意义 没有半毛钱关系,当初自己就是被这种错误的思想误导了很长时间。其实如果你愿意你,可以把 @Singleton 换成任意单词,什么 @Dog、@Cat、@XXx 都可以,你只要保证这个注解标记的 Component 在 App 进程中为单例的,并且得到正确的实现(被正确的标记到 类构造器 或 Module 中的 @Provides 标记的方法),那么它对应生成的类实例就是 单例 的。
@Singleton 之所以被默认实现,只是因为这可以让人根据它的字面意思,知道被他标记的相关生成的类实例为单例,这符合了 Java 的命名规范。

3. 举个栗子:

上面谈到的全都是理论,那么我们就是用相应的代码来验证他们。

3.1 自定义 Scope

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

这里为了表明最后的 单例 和 Scope 的命名没有任何关系,名字避免使用了容易给人造成疑惑的 ApplicationScope 、ActivityScope 等,而使用了 AnyOneScope,但是其实这些名字都是无所谓的 。

3.2 POJO – AppleBean

public class AppleBean {private String color;private int weight;public AppleBean() {Log.e("TAG", "AppleBean");}
}

3.3 POJO – OrgranBean

public class OrgranBean {private String color;private int weight;public OrgranBean() {Log.e("TAG", "OrgranBean");}
}

3.4 Module

@Module
public class FruitModule {@AnyOneScope@ProvidesAppleBean provideApple() {return new AppleBean();}@AnyOneScope@ProvidesOrgranBean provideOrgran() {return new OrgranBean();}
}

该 Module 提供 AppleBean 、OrgranBean 实例对象的方法,两个方法使用 @AnyOneScope 进行注解。

3.5 Component

@AnyOneScope
@Component(modules = {FruitModule.class})
public interface FruitComponent {void inject(FuriteScopeActivity mTestScopeActivity);
}

3.6 目标类 (注入类)

public class FuriteScopeActivity extends AppCompatActivity {@InjectAppleBean mAppleBeanA;@InjectAppleBean mAppleBeanB;@InjectOrgranBean mOrgranBeanA;@InjectOrgranBean mOrgranBeanB;FruitComponent mFruitComponent;@Overrideprotected 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.hirezy.dagger2demo.practiceFifth.DaggerFruitComponent@7c6e469
mAppleBeanA:com.hirezy.dagger2demo.practiceFifth.AppleBean@b7d6eee
mAppleBeanB:com.hirezy.dagger2demo.practiceFifth.AppleBean@b7d6eee
mOrgranBeanA:com.hirezy.dagger2demo.practiceFifth.OrgranBean@648ef8f
mOrgranBeanB:com.hirezy.dagger2demo.practiceFifth.OrgranBean@648ef8f

可以看到日志分别打印了 FruitComponent 的实例 – mFruitComponent,AppleBean 的两个实例 – mAppleBeanA、mAppleBeanB,OrgranBean 的两个实例 – mOrgranBeanA、mOrgranBeanB,发现 mAppleBeanA 和 mAppleBeanB 的哈希值相同即为 同一个对象、mOrgranBeanA、mOrgranBeanB 的哈希值相同即为 同一个对象,验证了我们对 @Scope 的认识 – 实现单例 。

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());}

3.7 运行程序查看日志信息:

03-10 22:15:54.903 14184-14184/com.hirezy.dagger2demo E/TAG: AppleBeanOrgranBeanmFruitComponent1:com.hirezy.dagger2demo.practiceFifth.DaggerFruitComponent@7c6e469mAppleBeanA:com.hirezy.dagger2demo.practiceFifth.AppleBean@b7d6eeemAppleBeanB:com.hirezy.dagger2demo.practiceFifth.AppleBean@b7d6eeemOrgranBeanA:com.hirezy.dagger2demo.practiceFifth.OrgranBean@648ef8f
03-10 22:15:54.904 14184-14184/com.hirezy.dagger2demo E/TAG: mOrgranBeanB:com.hirezy.dagger2demo.practiceFifth.OrgranBean@648ef8f

生成的对象和哈希值的对应关系为:

DaggerFruitComponent --> 7c6e469
AppleBean --> b7d6eee
OrgranBean --> 648ef8f

3.8 触发 jump() 方法

触发 jump() 方法后的日志信息如下:

03-10 22:16:22.624 14184-14184/com.hirezy.dagger2demo E/TAG: AppleBeanOrgranBeanmFruitComponent2:com.hirezy.dagger2demo.practiceFifth.DaggerFruitComponent@8196f9emAppleBeanA:com.hirezy.dagger2demo.practiceFifth.AppleBean@50ada7fmAppleBeanB:com.hirezy.dagger2demo.practiceFifth.AppleBean@50ada7fmOrgranBeanA:com.hirezy.dagger2demo.practiceFifth.OrgranBean@16bea4cmOrgranBeanB:com.hirezy.dagger2demo.practiceFifth.OrgranBean@16bea4c

生成的对象和哈希值的对应关系为:

FruitComponent --> 8196f9e
AppleBean --> 50ada7f
OrgranBean --> 16bea4c

很明显 AppleBean 、OrgranBean 重新生成了新的对象,难道不是单例了?难道上文的结论是错误的?其实不是这样的,因为 FruitComponent 生成了新的对象,所以其作用域下的类重新生成了新的实例。

证明了:在 Scope 注解标记 Component 的作用域内生产的实例是单例的。

4. 总结

至此,对 Dagger2 中的 Scope 的理解就如上文所示了。如果你是新手可以从头看Dagger 2 系列。

  1. Dagger 2 系列(一)——基础篇之依赖注入
  2. Dagger 2 系列(二)——@Inject、@Component 注解介绍
  3. Dagger 2 系列(三)——@Module 和 @Provides注解介绍
  4. Dagger 2 系列(四)——@Named 和 @Qualifier注解介绍

5. 参考资料

Dependency injection with Dagger 2 - the API

Dependency injection with Dagger 2 - Custom scopes

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31132.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

脑电波控制设备:基于典型相关分析(CCA)的脑机接口频率精准解码方法

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、CCA的用途二、频率求解思路三、输入数据结构四、判断方法五、matlab实践1.数据集获取及处理2.matlab代码3.运行及结果 六、参考文献 前言 在脑机接口(BCI)领…

fiddler+雷电模拟器(安卓9)+https配置

一、fiddler配置 1、开启https代理 2、根证书安装:导出证书系统安装 二、模拟器设置 1、设置网络桥接模式 【点击安装】提示安装成功后保存即可 2、开启root(开启adb远程调试) 3、开启磁盘写入 4、设置WLAN代理 5、证书安装:物…

跨越时空的对话:图灵与GPT-4聊AI的前世今生

(背景:虚拟咖啡厅,图灵身着1950年代西装,端着一杯热茶,GPT-4以全息投影形态坐在对面) 图灵(喝了口茶):“听说你能写诗?我当年在布莱切利园破解Enigma时&…

双击PPT文件界面灰色不可用,需要再次打开该PPT文件才能正常打开

双击PPT文件界面灰色不可用,需要再次打开该PPT文件才能正常打开 1. 软件环境⚙️2. 问题描述🔍3. 解决方法🐡解决步骤 4. 结果预览🤔 1. 软件环境⚙️ Windows10 或 Windows11 专业版64位,安装MotionGo软件&#xff08…

【时间序列聚类】Feature-driven Time Series Clustering(特征驱动的时间序列聚类)

文章目录 1.文章介绍2.问题背景3.拟解决的问题4.主要贡献5.提出的方法5.1模型pipeline5.2特征抽取和选择5.3图渲染和社区检测5.4共现矩阵的构建5.5对共现矩阵进行聚类 6.实验6.1模型设置6.2实验结果6.3消融实验 7.结论8.个人观点9.Reference 1.文章介绍 论文出处:ED…

tomcat负载均衡配置

这里拿Nginx和之前做的Tomcat 多实例来实现tomcat负载均衡 1.准备多实例与nginx tomcat单机多实例部署-CSDN博客 2.配置nginx做负载均衡 upstream tomcat{ server 192.168.60.11:8081; server 192.168.60.11:8082; server 192.168.60.11:8083; } ser…

SQLAlchemy系列教程:如何执行原生SQL

Python中的数据库交互提供了高级API。但是,有时您可能需要执行原始SQL以提高效率或利用数据库特定的特性。本指南介绍在SQLAlchemy框架内执行原始SQL。 在SQLAlchemy中执行原生SQL SQLAlchemy虽然以其对象-关系映射(ORM)功能而闻名&#xff…

19.HarmonyOS Next CustomSlider组件基础教程(一)

温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦! 1. 组件介绍 Slider(滑动选择器)是HarmonyOS中常用的交互组件,用于在给定的数值范围内进行连续值的选择。本教…

管中窥豹数字预失真(DPD)

管中窥豹数字预失真(DPD) 数字预失真在通信领域发挥了巨大的作用,对提高功放效率、改善误码率起了不可忽略的作用,广泛运用与通信、雷达等各种领域。但是对于普通用户,它显得及其高深神秘。今天就用这个短文&#xff…

MCP极简入门:超快速上手运行简单的MCP服务和MCP客户端

MCP是什么? 首先我们快速过一下MCP的基本概念,接着我们会通过一个简单的天气服务的教程,来上手学会使用MCP服务和在主机运行服务。本文根据官方教程改编。 1. MCP的基本概念 MCP(Model Context Protocol,模型上下文…

DeepSeek进阶应用(一):结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)

🌟前言: 在软件开发、项目管理和系统设计等领域,图表是表达复杂信息的有效工具。随着AI助手如DeepSeek的普及,我们现在可以更轻松地创建各种专业图表。 名人说:博观而约取,厚积而薄发。——苏轼《稼说送张琥》 创作者&…

海康线扫相机平场矫正教程

0、平场矫正前的准备确认 1、白纸准备 确保视野中有一张平整且无折痕的白纸,使其完全铺满相机的整个视野。 2、行高设置 将行高参数设定为 2048。 3、灰度值控制 相机端图像的灰度值应维持在 120 - 180 这个区间内。同时,最亮像素点与最暗像素点的灰度…

数智读书笔记系列015 探索思维黑箱:《心智社会:从细胞到人工智能,人类思维的优雅解读》读书笔记

引言 《The Society of Mind》(《心智社会》)的作者马文・明斯基(Marvin Minsky),是人工智能领域的先驱和奠基者之一 ,1969 年获得图灵奖,被广泛认为是对人工智能领域影响最大的科学家之一。他…

游戏引擎学习第148天

回顾并规划今天的工作 没有使用引擎,也没有任何库支持,只有我们自己,编写游戏的所有代码,不仅仅是小小的部分,而是从头到尾。现在,我们正处于一个我一直想做的任务中,虽然一切都需要按部就班&a…

bug-Ant中a-select的placeholder不生效(绑定默认值为undefined)

1.问题 Ant中使用a-select下拉框时,placeholder设置输入框显示默认值提示,vue2ant null与undefined在js中明确的区别: null:一个值被定义,定义为“空值” undefined:根本不存在定义 2.解决 2.1 a-select使…

DeepSeek教我写词典爬虫获取单词的音标和拼写

Python在爬虫领域展现出了卓越的功能性,不仅能够高效地抓取目标数据,还能便捷地将数据存储至本地。在众多Python爬虫应用中,词典数据的爬取尤为常见。接下来,我们将以dict.cn为例,详细演示如何编写一个用于爬取词典数据…

springboot-自定义注解

1.注解的概念 注解是一种能被添加到java代码中的【元数据,类、方法、变量、参数和包】都可以用注解来修饰。用来定义一个类、属性或一些方法,以便程序能被捕译处理。 相当于一个说明文件,告诉应用程序某个被注解的类或属性是什么&#xff0c…

低代码开发直聘管理系统

低代码 DeepSeek 组合的方式开发直聘管理系统,兼职是开挂的存在。整个管理后台系统 小程序端接口的输出,只花了两个星期不到。 一、技术栈 后端:SpringBoot mybatis MySQL Redis 前端:Vue elementui 二、整体效果 三、表结…

【面试】Kafka

Kafka 1、为什么要使用 kafka2、Kafka 的架构是怎么样的3、什么是 Kafka 的重平衡机制4、Kafka 几种选举过程5、Kafka 高水位了解过吗6、Kafka 如何保证消息不丢失7、Kafka 如何保证消息不重复消费8、Kafka 为什么这么快 1、为什么要使用 kafka 1. 解耦:在一个复杂…

文件操作详解(万字长文)

C语言文件操作 一、为什么使用文件?二、文件分类三、文件的打开和关闭四、文件的顺序读写4.1fputc4.2fgetc4.3fputs4.4fgets4.5 fprintf4.6 fscanf4.7 fwrite4.8 fread 五、文件的随机读写5.1 fseek5.2 ftell和rewind六、文件读取结束的判定七、文件缓冲区 一、为什…