ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【Android设计模式应用】 谈谈Android中的单例模式,程序员进阶

2022-01-31 17:01:51  阅读:198  来源: 互联网

标签:Singleton LayoutInflater private static 单例 Android 设计模式 public


//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
//私有的构造函数
private Singleton() {}
//私有的静态变量
private static Singleton single=null;
//暴露的公有静态方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}

  • 一般来说懒汉式分为三个部分,私有的构造方法,私有的全局静态变量,公有的静态方法
  • 起到重要作用的是静态修饰符static关键字,我们知道在程序中,任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序退出内存才会释放这个空间,因此也就保证了单例类的实例一旦创建,便不会被系统回收,除非手动设置为null。
  • 这种方式创建的缺点是存在线程不安全的问题,解决的办法就是使用synchronized 关键字,便是单例模式的第二种写法了。

懒汉式(线程安全)

public class Singleton {
//私有的静态变量
private static Singleton instance;
//私有的构造方法
private Singleton (){};
//公有的同步静态方法
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

  • 这种单例实现方式的getInstance()方法中添加了synchronized 关键字,也就是告诉Java(JVM)getInstance是一个同步方法。
  • 同步的意思是当两个并发线程访问同一个类中的这个synchronized同步方法时,一个时间内只能有一个线程得到执行,另一个线程必须等待当前线程执行完才能执行,因此同步方法使得线程安全,保证了单例只有唯一个实例。
  • 但是它的缺点在于每次调用getInstance()都进行同步,造成了不必要的同步开销。这种模式一般不建议使用。

饿汉式(线程安全)

//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton {
//static修饰的静态变量在内存中一旦创建,便永久存在
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

  • 饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。其中instance=new Singleton()可以写成:

static {
instance = new Singleton();
}

  • 属于变种的饿汉单例模式,也是基于classloder机制避免了多线程的同步问题,instance在类装载时就实例化了。

DCL双重校验模式

public class Singleton {
private static Singleton singleton; //静态变量
private Singleton (){} //私有构造函数
public static Singleton getInstance() {
if (singleton == null) { //第一层校验
synchronized (Singleton.class) {
if (singleton == null) { //第二层校验
singl
eton = new Singleton();
}
}
}
return singleton;
}
}

  • 这种模式的特殊之处在于getInstance()方法上,其中对singleton进行了两次判断是否空,第一层判断是为了避免不必要的同步,第二层的判断是为了在null的情况下才创建实例。

具体我们来分析一下: 假设线程A执行到了singleton = new Singleton(); 语句,这里看起来是一句代码,但是它并不是一个原子操作,这句代码最终会被编译成多条汇编指令,它大致会做三件事情:

  1. 给Singleton的实例分配内存
  2. 调用Singleton()的构造函数,初始化成员字段
  3. 将singleton对象指向分配的内存空间(即singleton不为空了)

但是在JDK1.5之后,官方给出了volatile关键字,将singleton定义的代码,为了解决DCL失效的问题。

private volatile static Singleton singleton; //使用volatile 关键字

静态内部类单例模式

public class Singleton {
private Singleton (){} ;//私有的构造函数
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
//定义的静态内部类
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton(); //创建实例的地方
}
}

  • 第一次加载Singleton 类的时候并不会初始化INSTANCE ,只有第一次调用Singleton 的getInstance()方法时才会导致INSTANCE 被初始化。
  • 因此,第一次调用getInstance()方法会导致虚拟机加载SingletonHolder 类,这种方式不仅能够确保单例对象的唯一性,同时也延迟了单例的实例化。

枚举单例

前面的几种单例模式实现方式,一般都会稍显麻烦,或是在某些特定的情况下出现一些状况。下面介绍枚举单例模式的实现:

public enum Singleton { //enum枚举类
INSTANCE;
public void whateverMethod() {

}
}

枚举单例模式最大的优点就是写法简单,枚举在java中与普通的类是一样的,不仅能够有字段,还能够有自己的方法,最重要的是默认枚举实例是线程安全的,并且在任何情况下,它都是一个单例。即使是在反序列化的过程,枚举单例也不会重新生成新的实例。而其他几种方式,必须加入如下方法:

private Object readResolve() throws ObjectStreamException{
return INSTANCE;
}

这样的话,才能保证反序列化时不会生成新的方法

使用容器实现单例模式

public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();//使用HashMap作为缓存容器
private Singleton() {
}
public static void registerService(String key, Objectinstance) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;//第一次是存入Map
}
}
public static ObjectgetService(String key) {
return objMap.get(key) ;//返回与key相对应的对象
}
}

  • 在程序的初始,将多种单例模式注入到一个统一的管理类中,在使用时根据key获取对应类型的对象。

场景应用

  1. 那么什么时候需要考虑使用单例模式呢?
  • 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
  1. 下面我们结合Android中一些源码来分析一下下

Android中常用的EventBus框架

  • 我们可以看看EventBus中的如何使用单例模式的,主要是使用双重检查DCL

static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}

这样的话它的资源利用率会很高,并且第一次执行的时候,是单例对象才会被实例化,但是第一次加载的时候会稍慢,可以被接受

LayouInflater的单例模式实现

  • 基本用法

LayoutInflater mInflater = LayoutInflater.from(this);

上边的写法估计没有人会陌生,获取LayoutInflater 的实例,我们一起看看具体的源码实现:

  1. 通过LayoutInflater.from(context)来获取LayoutInflater服务

/**

  • Obtains the LayoutInflater from the given context.
    */
    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;
    }
  1. 看看context.getSystemService是怎么工作的,context的实现类是ContextImpl类,点进去看一下

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

  1. 进入到SystemServiceRegistry类中

/**

  • Gets a system service from a given context.
    */
    public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
    }

  • 到这里了相信各位已经感觉这是上述说的使用容器实现单例模式了,对比一下,果然是

private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();

  • 使用map通过键值对的方式保存系统服务。在调用registerService的时候注入。

/**

  • Statically registers a system service with the context.
  • This method must be called during static initialization only.
    */
    private static void registerService(String serviceName, Class serviceClass,
    ServiceFetcher serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }
  1. 我们可以再看看这些服务都是在什么时候注册的

static {

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

注册的

static {

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

标签:Singleton,LayoutInflater,private,static,单例,Android,设计模式,public
来源: https://blog.csdn.net/m0_66264819/article/details/122760643

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有