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

2022/1/31 17:40:55

本文主要是介绍【Android设计模式应用】 谈谈Android中的单例模式,程序员进阶,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

//懒汉式单例类.在第一次调用的时候实例化自己
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());
}});
}



这篇关于【Android设计模式应用】 谈谈Android中的单例模式,程序员进阶的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程