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
    @Provides
    AppleBean provideApple() {
        return new AppleBean();
    }

    @AnyOneScope
    @Provides
    OrgranBean 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 {

    @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.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: AppleBean
    OrgranBean
    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
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: AppleBean
    OrgranBean
    mFruitComponent2:com.hirezy.dagger2demo.practiceFifth.DaggerFruitComponent@8196f9e
    mAppleBeanA:com.hirezy.dagger2demo.practiceFifth.AppleBean@50ada7f
    mAppleBeanB:com.hirezy.dagger2demo.practiceFifth.AppleBean@50ada7f
    mOrgranBeanA:com.hirezy.dagger2demo.practiceFifth.OrgranBean@16bea4c
    mOrgranBeanB: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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32初始安装

前言 很多人刚买来STM32就迫不及待地想要用它来写程序,看见STM32开发版和ST-Link上有几个插口就直接连接,结果就像我一样一不小心就导致ST -Link烧坏了😂 所以本篇博客将做最基础的但是对于小白来说最重要的教学,STM32的线路连接…

爱普生温补晶振 TG5032CFN高精度稳定时钟的典范

在科技日新月异的当下,众多领域对时钟信号的稳定性与精准度提出了极为严苛的要求。爱普生温补晶振TG5032CFN是一款高稳定性温度补偿晶体振荡器(TCXO)。该器件通过内置温度补偿电路,有效抑制环境温度变化对频率稳定性的影响&#x…

深入理解C语言链表:数据结构的基石

在C语言的编程宇宙中,链表就像是一座稳固的基石,支撑着众多复杂程序的构建。它以独特的魅力和强大的功能,在解决各类编程难题时发挥着至关重要的作用。今天,就让我们一同深入探索链表的奥秘。 目录 一、链表初相识 二、链表的结…

从头开始开发基于虹软SDK的人脸识别考勤系统(python+RTSP开源)(二)

今天咱们继续昨天的话题,今天的重点是看思路和代码了。废话不多说,直接上干货。 先说一句,为了省事,直接一个文件完成所有功能,可能在代码可读性上差一些,比较眼花缭乱哈哈。整个文件含空行代码共1931行&a…

报表控件stimulsoft操作:使用 Angular 应用程序的报告查看器组件

Stimulsoft Ultimate (原Stimulsoft Reports.Ultimate)是用于创建报表和仪表板的通用工具集。该产品包括用于WinForms、ASP.NET、.NET Core、JavaScript、WPF、PHP、Java和其他环境的完整工具集。无需比较产品功能,Stimulsoft Ultimate包含了…

【网络编程】WSAAsyncSelect 模型

十、基于I/O模型的网络开发 接着上次的博客继续分享:select模型 10.8 异步选择模型WSAAsyncSelect 10.8.1 基本概念 WSAAsyncSelect模型是Windows socket的一个异步I/O 模型,利用这个模型,应用程序 可在一个套接字上接收以Windows 消息为基…

从0开始的操作系统手搓教程43——实现一个简单的shell

目录 添加 read 系统调用,获取键盘输入 :sys_read putchar和clear 上班:实现一个简单的shell 测试上电 我们下面来实现一个简单的shell 添加 read 系统调用,获取键盘输入 :sys_read /* Read count bytes from the file pointed to by fi…

鸿蒙应用开发—数据持久化之SQLite

文章目录 SQLite简介创建数据库添加数据查询数据更新数据删除数据升级数据库使用事务参考 SQLite简介 SQLite是一个轻量级关系数据库,占用资源很少,只有几百KB的大小,无需服务器支撑,是一个零配置、事务性的SQL数据库引擎。 相对…

应急响应--流量分析

(一)Cobalt Strike流量特征分析 1.HTTP特征 源码特征: 在流量中,通过http协议的url路径,在checksum8解密算法计算后,32位的后门得到的结果是92,64位的后门得到的结果是93,该特征符…

初始化E9环境,安装Sqlserver数据库

title: 初始化E9环境,安装Sqlserver数据库 date: 2025-03-10 19:27:19 tags: E9SqlServer初始化E9环境,安装Sqlserver数据库 安装E9本地环境安装Sql server 数据库1、检查SQL Server服务是否开启2、检查SQL Server网络网络配置是否开启创建一个ecology数据库点击初始化数据库…

自然语言处理:无监督朴素贝叶斯模型

介绍 大家好,博主又来和大家分享自然语言处理领域的知识了,今天给大家介绍的是无监督朴素贝叶斯模型。 在自然语言处理这个充满挑战又极具魅力的领域,如何从海量的文本数据中挖掘有价值的信息,一直是研究者们不断探索的课题。无…

API调试工具的无解困境:白名单、动态IP与平台设计问题

引言 你是否曾经在开发中遇到过这样的尴尬情形:你打开了平台的API调试工具,准备一番操作,结果却发现根本无法连接到平台?别急,问题出在调试工具本身。今天我们要吐槽的就是那些神奇的开放平台API调试工具,…

VSCode 2025最新前端开发必备插件推荐汇总(提效指南)

🌟前言: 如果你是一名前端开发工程师,合适的开发工具能大大提高工作效率。Visual Studio Code (VSCode) 凭借其轻量级、高扩展性的特点,已成为众多前端开发者在win系电脑的首选IDE。 名人说:博观而约取,厚积而薄发。—…

小程序事件系统 —— 33 事件传参 - data-*自定义数据

事件传参:在触发事件时,将一些数据作为参数传递给事件处理函数的过程,就是事件传参; 在微信小程序中,我们经常会在组件上添加一些自定义数据,然后在事件处理函数中获取这些自定义数据,从而完成…

初阶数据结构(C语言实现)——4.2队列

目录 2.队列2.1队列的概念及结构2.2队列的实现2.2.1 初始化队列2.2.2 销毁队列2.2.3 队尾入队列2.2.4 队头出队列2.2.5获取队列头部元素2.2.6 获取队列队尾元素2.2.7获取队列中有效元素个数2.2.8 检测队列是否为空,如果为空返回非零结果,如果非空返回0 3…

linux 命令 cat

cat 是 Linux 中用于查看、创建和合并文件的常用命令,全称 concatenate(连接)。其核心功能是将文件内容输出到终端或重定向到其他文件/命令中。以下是详细用法及场景示例: 基本语法 cat [选项] [文件1] [文件2] ... 选项…

TON基金会确认冠名赞助2025香港Web3嘉年华,并将于4月8日重磅呈现“TON生态日”

近日,由万向区块链实验室与HashKey Group联合推出的Web3年度盛典——2025香港Web3嘉年华正式宣布,TON基金会确认成为本届嘉年华的冠名赞助商,并将于4月8日在主会场特别举办“TON生态日”专题Side Event,集中展现TON生态的最新技术…

【Java代码审计 | 第七篇】文件上传漏洞成因及防范

未经许可,不得转载。 文章目录 文件上传漏洞漏洞成因未验证文件类型和扩展名未限制文件上传路径 防范验证文件类型和扩展名验证文件内容限制文件上传路径使用安全的文件上传库 标准代码 文件上传漏洞 文件上传漏洞是指攻击者通过上传恶意文件(如可执行脚…

【无人机路径规划】基于麻雀搜索算法(SSA)的无人机路径规划(Matlab)

效果一览 代码获取私信博主基于麻雀搜索算法(SSA)的无人机路径规划(Matlab) 一、算法背景与核心思想 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种受麻雀群体觅食行为启发的元启发式算法&#xff0…

狮子座大数据分析(python爬虫版)

十二星座爱情性格 - 星座屋 首先找到一个星座网站,作为基础内容,来获取信息 网页爬取与信息提取 我们首先利用爬虫技术(如 Python 中的 requests 与 BeautifulSoup 库)获取页面内容。该页面(xzw.com/astro/leo/&…