【设计模式与Android】单例模式——独一无二的皇帝

原创 2017年08月30日 21:08:06

什么是单例模式

 

所谓单例模式,就是确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例的设计模式。单例模式是最简单的设计模式,也是应用最广的设计模式。一般用于避免产生多个对象消耗过多的资源或者某种类型的对象必须独一无二的情景。

 

单例模式的实现方式

 

(1)饿汉式

单例模式极其简单,仅有一个单例类。既然常用于确保某种类型的对象必须独一无二的情景,那么我们可以用皇帝来举例。代码如下:

public class Emperor {

    //初始化一个皇帝,国不可一日无君
    
private static final Emperor emperor = new Emperor();

    //防止刁民冒充皇帝
    
private Emperor(){}

    public static Emperor getEmperor(){
        return emperor;
    }

}

 

从上述代码中可以看到,类不能通过new的形式构造对象,只能用方法来获取唯一的静态对象。这种在声明的时候就初始化的实现方式就叫做饿汉式。

 

(2)懒汉式

与饿汉式不同,懒汉式只有在第一次调用方式时才进行初始化。实现代码如下:

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

 

懒汉式在方法中添加了synchronized关键字,可以在多线程情况下确保单例对象独一无二。但即使已经被初始化,每次调用还会进行同步,会消耗不必要的资源,并且第一次加载时进行实例化会拖慢反应速度,因此懒汉式一般不建议使用。但懒汉式并非一无是处,如果一直没有人用的话,就不会创建实例,则是节约空间。这是以时间换空间的实现方式,与饿汉式的以空间换时间各有所长。

 

还记得语文老师讲课外文学知识题的“禅杖就是鲁智深,戒刀就是武松,板斧就是李逵”的规律吗?注意上面代码的getIntance()方法,实战中见到getIntance()就是单例模式的准确率八九不离十。

 

(3)DCL式

Double Check Lock(以下简称DLC)实现单例模式既能够在需要时才初始化对象,又能保证线程安全。代码如下:

public class Singleton {

    private static Singleton instance;

    private Singleton(){}

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

}

 

DCL可以保证无论何时读取这个变量,都是读到内存中最新的值,无论何时写这个变量,都可以立即写到内存中。DCL是目前单例模式最常见的实现方式。

(4)静态内部类式

DCL尽管能完美解决资源消耗、同步多余、线程不安全的问题,却有低概率在并发场景比较复杂的情况下失效(少见于J2EE和Hadoop等场景,绝少见于Android场景)。因此在对性能要求极高的情况下我们可以采取静态内部类式来实现单例模式。代码如下:

public class Singleton {

    private Singleton(){}

    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
    
    private static class SingletonHolder{
        private static final Singleton instance = new Singleton();
    }

}

 

 

静态内部类式利用ClassLoader机制来保证初始化时仅有一个线程,不但不会造成性能损耗,还是天衣无缝的安全方式。

 

(5)单例模式的容器式管理

还是拿皇帝举例子,天下可能有多个皇帝,一个软件也可能有多个单例对象。举唐玄宗李隆基和大燕皇帝安禄山的对立的例子不是太恰当,毕竟几乎没有史书认同安禄山是合法皇帝。我就举一个大家耳熟能详的例子——《三国演义》第80回讲述了中国历史上第一次同时存在两位被后世的历史学家认定为合法皇帝(曹丕和刘备)的局面,也是最著名的一次。我们先建立一个管理类:

public class EmperorManager {

    public static final String WEI = "魏";
    public static final String SHU = "蜀";
    public static final String WU = "吴";
    
    private static Map<String,Object> emperors = new HashMap<>();

    private EmperorManager(){}

    public static void ascendEmperor(String key,Object emperor){
        if (!emperors.containsKey(key)){
            emperors.put(key,emperor);
        }
    }
    
    public static Object getEmperor(String key){
        return emperors.get(key);
    }

}

然后就可以管理多个单例对象了:

//曹丕废帝篡炎刘
EmperorManager.ascendEmperor(EmperorManager.WEI,WeiEmperor.getEmperor());
//汉王正位续大统
EmperorManager.ascendEmperor(EmperorManager.SHU,ShuEmperor.getEmperor());

几年后,孙权登基,天下又有了新的皇帝,写法以此类推。

 

Android源码中的单例模式

 

(1)Application

Application是Android中最典型,也是最常见的单例模式。用户重写Application类也只重写一个。

 

(2)Activity

Activity在singleInstance启动模式下只有一个实例,并且这个实例独立运行在一个Task中,不允许有别的Activity存在,这也是一种单例模式。

 

(3)Service

Service用bindService()启动之后,无论再启动多少次,都只会调用onStartCommand()而不会再调用onCreate(),因为每次调用的Service都是同一对象。

 

(4)各种Manager

Android中有很多管理类,比如WindowManager、PowerManager、SensorManager、ActivityManager、StorageManager以及ServiceManager等等,这些管理类分别对某些资源进行操作,为了避免对同一资源的同时操作,也为了节约资源,都采取了单例模式。

 

(5)UID

在Picasso和Glide等框架流行起来之前,最常见的图片加载框架非Universal-Image-Loader(以下简称UID)莫属,UID的初始化方式如下:

//全局初始化此配置
ImageLoader.getInstance().init(config);

 

这里又见到了熟悉的getIntance(),根据思维定式判定这是单例模式。

 

Android开发中如何利用单例模式

 

(1)当创建一个对象需要较多资源时,比如读取配置或依赖较多其他对象时,可以用创建一个单例对象常驻内存的方式解决这个问题。

 

(2)当一个对象需要经常调用所以需要反复创建、销毁时,为了减少内存开支,可以用单例模式来减少创建、销毁该对象的资源浪费。

 

(3)当需要对同一个资源进行操作时(例如File I/O),可以创建一个FileManager,这样内存里只有一个实例,避免了对同一个资源的同时操作。

 

需要注意的几个问题

 

(1)单例模式必然有static修饰符,如果持有Activity的Context,很容易造成OOM,因此尽量使用Application的Context;此外有多少初学者在Activity销毁时忘记销毁视频或地图的单例对象而吃了大亏?

 

(2)在不需要独一无二的对象的时候不要采用单例模式,譬如自定义控件就是最不适合单例模式的场景。

版权声明:本文为博主原创文章,转载请以超链接形式标明文章原始出处和作者

【玖哥乱弹】成功的IT人士这样转型AI

AlphaGo在与围棋世界冠军的对弈大获全胜,不但让我们领略到了AI的巨大潜力,还把AI推上了新的浪潮之巅。作为一个从即将过去的移动互联网时代走来的Android工程师,我深深感受到自己成了传统行业,...

AVL 树插入算法记忆技巧总结

AVL 树在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加...

Java设计模式:单例模式-独一无二的对象

概念: 单例模式: 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 用处: 有一些对象我们只需要一个,例如:线程池(threadpool),缓存(cache),对话框,注...

单例模式——皇帝XXX

秦王嬴政统一中国,认为自己“德兼三皇、功盖五帝”,创“皇帝”一词作为华夏最高统治者的正式称号。自此“皇帝”这个称号沿袭了两千多年。皇帝每天的任务是接待众多的臣子,而众多臣子每天我面对同一个皇帝。臣子们...

单例模式(Singleton Pattern)——独一无二的对象

前言 概述 定义 注意 类图 设计模式的实现 懒汉式线程不安全 懒汉式线程安全 饿汉式线程安全但是不是延迟加载 双检锁双重校验锁DCL即double-checked locking线程安全 登记式静态...

设计模式——单例模式

  • 2011年04月16日 21:55
  • 39KB
  • 下载

设计模式——单例模式

  • 2013年08月11日 14:59
  • 4KB
  • 下载

模式五:单例模式(Singleton Pattern)——创建独一无二的对象

单例模式 确保一个类只有一个实例,并提供一个全局访问点。 应用分析 还有一些对象其实我们只需要一个,比如说:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置和注册表的对象、日志...

设计模式实现——单例模式

  • 2016年09月03日 19:28
  • 18.2MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【设计模式与Android】单例模式——独一无二的皇帝
举报原因:
原因补充:

(最多只允许输入30个字)