Android设计模式详解之单例模式

news2025/2/5 19:59:39

前言

定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

使用场景:确保某个类有且仅有一个对象的场景,避免产生多个对象消耗过多的资源。比如要访问IO和数据库资源,应该考虑使用单例模式。

UML类图:
单例UML图

实现方式

饿汉模式

/**
 * 饿汉单例
 */
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

}

优点:对象优先创建,无须等待,效率高。
缺点:申明静态对象的时候就已经初始化,一定程度上造成了资源的浪费。

懒汉模式

/**
 * 懒汉单例
 */
public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }

        return instance;
    }

}

优点:只有在使用时才会被实例化,一定程度上节约了资源。
缺点:第一次加载时需要等待,同时每一次调用getInstance()都进行同步,造成不必要的同步开销。

DCL(Double Check Lock)单例

/**
 * DCL单例
 */
public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

volatile关键字的作用:

  1. 禁止指令重排序;
    instance = new Singleton();这句代码实际上并不是一个原子操作,最终会被编译成多条汇编指令,大致做了如下3件事;
  • (1)给Singleton的实例分配内存;
  • (2)调用Singleton()的构造函数,初始化成员字段;
  • (3)将instance对象指向分配的内存空间(此时instance不再为null)

但是Java编译器允许处理器乱序执行,以上的执行顺序可能是1-2-3也可能是1-3-2,如果是后者,在线程A中当3执行完毕2未执行时,如果切换到线程B,此时instance已经非空,线程B直接取走instance,在使用就会出错,这就会导致DCL失效!!

  1. 线程可见性,及时更新实例到主内存;【JMM Java内存模型】

getInstance()方法中两次判空的作用:

第一次:避免重复加锁,造成资源浪费;
第二次:避免对象重复实例化;

优点:资源利用率高,避免不必要的同步;
缺点:第一次加载效率低,在某些情况下会出现DCL失效问题

静态内部类单例

/**
 * 静态内部类单例
 */
public class Singleton {
    private Singleton() {

    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }

}

优点:懒加载,能够确保线程安全,推荐使用这种单例模式实现方式!

容器实现单例模式

/**
 * 容器实现单例模式
 * on 2022/12/20
 */
public class SingletonManager {
    private static Map<String, Object> singletonMap = new HashMap<>();

    private SingletonManager() {
    }

    public static void registerService(String key, Object instance) {
        if (!singletonMap.containsKey(key)) {
            singletonMap.put(key, instance);
        }
    }

    public static Object getService(String key) {
        return singletonMap.get(key);
    }
}

优点:可以管理多种类型的单例,使用时可以通过统一的接口进行获取操作,降低用户使用成本,也对用户隐藏了具体实现,降低耦合度。

Android源码中的单例模式

  1. Context.getSystemService(String name)

这段代码我们经常使用去获取各种系统service,其实底层的具体实现就使用到了容器单例模式,接下来我们就跟踪下源码,看下具体实现;
我们知道Context是抽象类,他的实现类是ContextImpl,其中ContextImpl.getSystemService(String name)方法如下:

    public Object getSystemService(String name) {
		...
        return SystemServiceRegistry.getSystemService(this, name);
    }

我们继续分析SystemServiceRegistry.getSystemService(this, name)

    public static Object getSystemService(ContextImpl ctx, String name) {
        if (name == null) {
            return null;
        }
        final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        if (fetcher == null) {
        	...
            return null;
        }

        final Object ret = fetcher.getService(ctx);
        if (sEnableServiceNotFoundWtf && ret == null) {
			...
            return null;
        }
        return ret;
    }

重点看下SYSTEM_SERVICE_FETCHERS实例

		...
    private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new ArrayMap<String, ServiceFetcher<?>>();
		...
 static {
 ...
   registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
                new CachedServiceFetcher<ActivityManager>() {
            @Override
            public ActivityManager createService(ContextImpl ctx) {
                return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
            }});

        registerService(Context.ACTIVITY_TASK_SERVICE, ActivityTaskManager.class,
                new CachedServiceFetcher<ActivityTaskManager>() {
            @Override
            public ActivityTaskManager createService(ContextImpl ctx) {
                return new ActivityTaskManager(
                        ctx.getOuterContext(), ctx.mMainThread.getHandler());
            }});
	...
 }
        
	//注册service
 private static <T> void registerService(@NonNull String serviceName,
            @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
        SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
    }    

可以看到SYSTEM_SERVICE_FETCHERS是一个静态的ArrayMap实例,在static静态代码块中,对系统中的各个服务进行注册,如我们熟悉的ACTIVITY_SERVICEACTIVITY_TASK_SERVICELAYOUT_INFLATER_SERVICE等等,使用的时候根据serviceNameArrayMap中查找到对应Service,
显然,这里的各个服务是单例的!~~

  1. LayoutInfater实例单例

我们经常使用LayoutInfater去绑定布局文件,如LayoutInflater.from(this).inflate(),但实际上LayoutInflater
对象也是个单例的,接下来我们就从源码的角度去验证;
LayoutInflater是个抽象类,他的实现类是PhoneLayoutInflater,我们还是回过头看下SystemServiceRegistry

static{
    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
}

在静态代码块中同样是实例化了全局唯一的PhoneLayoutInflater对象,而我们调用的地方:

 public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

正是通过LAYOUT_INFLATER_SERVICE去查找对应的Service对象;

总结

单例模式是运用频率很高的模式;

它有以下优点:

  1. 内存中只有一个实例,减少了内存开销;
  2. 可以避免对资源的多重占用;
  3. 可以全局共享资源,方便数据访问;

同时他的缺点也显而易见:

  1. 一般没有接口,扩展性较差,扩展只能通过修改代码来实现;
  2. 如果持有Context,容易引发内存泄漏,注意传递给单例对象的Context应为Application Context

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA开发(数据库表设计)

对于大型系统数据库设计&#xff0c;需要进行一定的规划和规范&#xff0c;才能方便系统扩展和维护。一般系统的数据库设计要求&#xff0c;有数据库表系统规划&#xff0c;数据库表系统命名规范和设计规范。 一、数据库表系统规划 1、按系统规划或者按微服务规划 2、按业务…

Matlab论文插图绘制模板第71期—三维饼图(Pie3)

在之前的文章中&#xff0c;分享了Matlab饼图的绘制模板&#xff1a; 进一步&#xff0c;再来分享一下三维饼图的绘制模板。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;Matlab论文插图绘制模板系列&#xff0c;旨在降低大家使用Matlab进行科研绘图的门槛&#xff0…

最大子段和(动态规划详细解析)

最大子段和 题目描述 给出一个长度为 nnn 的序列 aaa&#xff0c;选出其中连续且非空的一段使得这段和最大。 输入格式 第一行是一个整数&#xff0c;表示序列的长度 nnn。 第二行有 nnn 个整数&#xff0c;第 iii 个整数表示序列的第 iii 个数字 aia_iai​。 输出格式 …

读取excel文件read_excel()--Pandas

1. 函数功能 读取excel文件&#xff0c;支持读取xls,xlsx,xlsm等类型的EXCEL文件。能够读取一个sheet表或多个sheet表 2. 函数语法 pandas.read_excel(io, sheet_name0, *, header0, namesNone, index_colNone, usecolsNone, squeezeNone, dtypeNone, engineNone, converte…

MCMC算法

一. MCMC算法定义 MCMC是Markov chain Monte Carlo的缩写&#xff0c;即马尔可夫链蒙特卡罗方法。MCMC是一组利用马尔可夫链从随机分布中取样的算法。生成的马氏链即是对于目标分布的近似估计。常见算法&#xff1a; Metropolis-Hastings算法Gibbs取样算法Hamiltonian Monte C…

单片机AT89C51数码管数字时钟和闹钟二

详细代码讨论加我QQ&#xff1a;1271370903 一、课题的方案设计与论证 1.1摘要 近年来随着计算机在社会领域的渗透和大规模集成电路的发展&#xff0c;单片机的应用正在不断地走向深入&#xff0c;由于它具有功能强&#xff0c;体积小&#xff0c;功耗低&#xff0c;价格便宜…

【我的渲染技术进阶之旅】你可能永远猜不到为什么Filament项目命名为TNT?

文章目录一、疑惑为啥叫TNT&#xff1f;二、寻找真相2.1 百度TNT关键字2.2 GitHub issue2.3 GitHub Discussion三、总结一、疑惑为啥叫TNT&#xff1f; 在我之前的博客【我的渲染技术进阶之旅】如何编译Filament的windows版本程序&#xff1f; 有介绍如何编译Windows版本的Fil…

React 18:Ref(获取DOM对象)

ref介绍 React中所有的操作默认都是在React元素上进行&#xff0c;然后再通过虚拟DOM应用到真实页面上的。这样做的好处我们不在赘述。 虽然如此&#xff0c;在React中依然为我们提供了可以直接访问原生DOM对象的方式。ref就是干这个事的。 ref是reference的简写&#xff0c…

【排序】详细聊聊归并排序(含非递归)

目录 归并排序的基本思想&#xff1a; 递归算法&#xff1a; 递归算法的思路分析&#xff1a; 开辟数组的函数&#xff1a; 递归的函数&#xff1a; 非递归算法&#xff1a; 非递归的思路分析&#xff1a; 边界问题&#xff1a; 时间复杂度和空间复杂度分析&#xff1a…

重建农场2.0:实景三维数据中心一站式解决方案

面向实景三维中国建设&#xff0c;如何扩大产能&#xff0c;不断提升实景三维数据中心的重建算力水平&#xff1f;如何满足快速迭代的需求&#xff0c;不断提升数据中心的应变能力&#xff1f;如何做到“一机多能”&#xff0c;不断外延数据中心的硬件价值&#xff1f;在前不久…

jquery获取父/子iframe页面的URL

最近因为要演示,做个临时ifame框架页面,因此子页面要根据父页面url来指定跳转。下面为ifame页面: 1、获取子页面url: var currentpath = window.location.pathname; console.log(currentpath); 输出为:/JG/TJ_JILU.aspx 2、获取父页面url: let currentTopHref = wind…

IDEA 注释模版

类的主注释 /*** description TODO* author Gaoxueyong* date ${DATE} ${TIME}* version 1.0*/方法的注释 1、创建自己的分组 选择右侧Template Group并输入名称 2、创建自己的模版 选择自己创建的分组然后选择Live Template 然后在Template text框内写入 *** $description…

脑图谱的一致性问题

脑图谱的意义及一致性问题 我们如何定义大脑的解剖结构&#xff0c;并将这些结构与大脑的功能联系起来&#xff0c;可以限制或增强我们对行为和神经系统疾病的理解大量可用的脑图谱给研究健康群体和患病人群的可重复性和描述大脑不同区域参与各种疾病的荟萃分析带来了问题——…

全力推进企业数智赋能发展主线,低代码任重道远

信息化进程 纵观近半个世纪以来我国信息化的发展&#xff0c;经历了20世纪80年代以个人计算机普及应用为特征的数字化阶段&#xff08;信息化1.0&#xff09;&#xff1b;20世纪90年代中期以互联网大规模商用为特征的网络化阶段&#xff08;信息化2.0&#xff09;&#xff1b;…

毕业设计 基于java web的网上招标系统

文章目录前言一、项目设计1. 模块设计注册用户部分管理员部分2. 实现效果二、部分源码最后前言 今天学长向大家分享一个 毕业设计项目: 基于java web的网上招标系统 一、项目设计 1. 模块设计 注册用户部分 1&#xff1a;查看网站流程&#xff1a;查看与网站有关的流程信息…

ZBC成功上线PancakeSwap的糖浆池,并有望在不久上线Binance

近期流支付协议Zebec Protocol正在成为加密行业备受瞩目的Web3生态&#xff0c;其在此前与Visa合作推出可以使用加密货币进行支付的借记卡Zebec Card后&#xff0c;得到了Visa创始团队成员在技术、市场发展前景上的高度认可。与此同时&#xff0c;Zebec Protcocol也与尼泊尔财政…

【react-脚手架】

目录bug初始化脚手架react脚手架创建项目并启动脚手架文件介绍案例&#xff1a;hello react快捷键组件化编码流程脚手架配置代理方法前置说明常用的ajax请求库配置方法一&#xff1a;只能配置一个路径配置方法二&#xff1a;配置多个路径引入bootsrtrap消息订阅与发布&#xff…

YOLO系列目标检测算法——YOLOX

YOLO系列目标检测算法目录 - 文章链接 YOLO系列目标检测算法总结对比- 文章链接 YOLOv1- 文章链接 YOLOv2- 文章链接 YOLOv3- 文章链接 YOLOv4- 文章链接 Scaled-YOLOv4- 文章链接 YOLOv5- 文章链接 YOLOv6- 文章链接 YOLOv7- 文章链接 PP-YOLO- 文章链接 …

毕业设计 基于java web的企业财务管理系统设计与实现

文章目录前言一、项目设计1. 模块设计2. 实现效果二、部分源码项目源码前言 今天学长向大家分享一个 java web 项目: 基于java web的企业财务管理系统设计与实现 适用于毕业设计、课程设计 一、项目设计 1. 模块设计 管理员的所有模块的功能分析&#xff1a; 部门信息管理…

2022年最火的8种编程语言~工作机会超多~

当今&#xff0c;我们已知的编程语言多达几百种&#xff0c;但是常被大家使用的只占少数&#xff0c;无论你是刚入行的新手还是一名成熟的开发者&#xff0c;了解编程语言的受欢迎程度都很有必要。 最近&#xff0c;国外网站DevJobsScanner公布了一项数据&#xff0c;他们在过…