Android设计模式之单例模式

来源:互联网 发布:淘宝销售属性是什么 编辑:程序博客网 时间:2024/05/29 11:54

本文为转载文章,转载出处:http://blog.csdn.net/smartbetter/article/details/68953251

对于很多从事 Android 开发的人来说,学习 Android 最大的障碍往往是对设计模式的理解而非源码本身,从今天开始,将主要开始分享 Android 设计模式实战中的应用,这将会是一系列的文章,相信会对很多人受益匪浅。

单例设计模式可以说是应用程序中应用最广的模式之一,在应用单例模式时,单例对象的类必须保证只有一个实例存在,而且可以自行实例化并向整个系统提供这个实例。一般在不能自由构造对象的情况下,就会使用单例设计模式,例如创建一个对象需要消耗资源过多,还有访问IO和数据库等资源等情况。

1.面向对象的六大原则

设计模式本身并不复杂,但学习设计模式是程序员自我修炼、提升实力过程中必不可少的一环,首先我们从面向对象的六大原则说起,面向对象的六大原则有:

1)单一职责原则,一个类中,应该是一组组相关性很高的函数、数据的封装;2)开闭灵活,软件中的对象应该对于扩展是开放的,而对于修改是封闭的;3)里氏替换原则,所有能引用基类的地方必须透明地使用其子类的对象;4)依赖倒置原则,指代了一种特殊的解耦形式,高层模块不应该依赖底层模块,两者都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象;5)接口隔离原则,客户端不应该依赖它不需要的接口;6)迪米特原则(最少知识原则),一个对象应该对其他对象有最少的了解。

在应用开发过程中,最难的不是完成应用的开发工作,而是在后续的升级、维护过程中让应用能够拥抱变化。意味着在满足需求且不破坏系统稳定性的前提下保持高可扩展性、高内聚、低耦合的系统架构。遵循面向对象六大原则就是我们走向灵活软件之路的第一步。

2.单例模式的优缺点

-单例模式优点在内存中只有一个实例,减少了内存开支和性能开销;可以避免对同一资源文件的同时写操作;可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。缺点一般没有接口,扩展困难;单例模式如果持有 Context,很容易引发内存泄漏,此时需要注意传递给单例对象的 Context 最好是 Application Context。

3.单例模式的UML类图

单例模式的UML类图

4.单例模式的几种实现方式

1.饿汉式

public class Singleton {    /**     * 单例模式(饿汉式)     */    private static final Singleton instance = new Singleton(); // 句柄    private Singleton() {    }    public static Singleton getInstance() {        return instance;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.懒汉式

优点是单例只有在使用时才会被实例化;缺点是第一次加载反应稍慢,每次调用 
getInstance() 都进行同步,造成不必要的同步开销,一般不建议使用

public class Singleton {    /**     * 单例模式(懒汉式)     */    private static Singleton instance; // 句柄    private Singleton() {    }    public static synchronized Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3.DCL方式

Double Check Lock 实现单例,优点是既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用 getInstance() 不进行同步锁;缺点是第一次加载反应稍慢。

public class Singleton {    /**     * 单例模式(DCL方式)     */    private volatile static Singleton instance = null; // 句柄    private Singleton() {    }    public static Singleton getInstance() {        if (instance == null) { // 避免不必要的同步            synchronized (Singleton.class) {                if (instance == null) {                    instance = new Singleton();                }            }        }        return instance;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4.静态内部类方式——推荐使用

第一次加载不会初始化 Singleton,只有在第一次调用 Singleton 的 getInstance() 才会初始化,第一次调用会导致虚拟机加载 SingletonHolder 类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,推荐使用

public class Singleton {    /**     * 单例模式(静态内部类的方式)     */    private Singleton() {    }    public static Singleton getInstance() {        return SingletonHolder.instance;    }    private static class SingletonHolder {        private static final Singleton instance = new Singleton();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

5.使用容器实现单例模式

在初始,将多种单例类型注入到一个统一的管理类中,在使用时根据 key 获取对象对应类型的对象。可管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了耦合度。

public class SingletonManager {    /**     * 单例模式(使用容器实现单例模式)     */    private static Map<String, Object> objMap = new HashMap<>();    private SingletonManager() {    }    public static void registerInstance(String key, Object instance) {        if (objMap.containsKey(key)) {            objMap.put(key, instance);        }    }    public static Object getInstance(String key) {        return objMap.get(key);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

不管以何种形式实现单例模式,它们的核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题,选择何种取决于项目本身,并发环境是否复杂、单例对象等资源消耗等。

序列化:将一个对象的实例写到磁盘,然后再读回来,从而获得一个实例。

5.Android系统源代码中的应用情景

在Android系统中,我们经常通过 Context 获取系统级别的服务,如 WindowsManagerService、ActivityManagerService 等,更常用的是 LayoutInflater 的类:

LayoutInflater.from(context).inflate(layoutid, null);
  • 1
  • 1

这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过 getSystemService(String name) 获取。

我们可以看一下 LayoutInflater.from(context) 的实现:

/** * 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

我们跟踪到 Context 类,发现 Context 是抽象类:

public abstract class Context {}
  • 1
  • 2
  • 1
  • 2

Context 的总个数 = Activity 个数 + Service 个数 + 1。

一个 Activity 的入口是 ActivityThread 的 main 函数,在 main 函数中创建一个新的 ActivityThread 对象(Framework层),并且启动消息循环(UI线程),创建新的 Activity、新的 Context 对象,然后将该 Context 对象传递给 Activity。并且我们可以通过 分析 ActivityThread 源代码知道,Context 的实现类是 ContextImpl(Framework层):

class ContextImpl extends Context {    // 代码省略    // 服务提取器 ServiceFetcher 通过 getService 获取服务对象    static class ServiceFetcher {        int mContextCacheIndex = -1;        // 获取系统服务        public Object getService(ContextImpl ctx) {            ArrayList<Object> cache = ctx.mServiceCache;            Object service;            synchronized (cache) {                if (cache.size() == 0) {                    for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) {                        cache.add(null);                    }                } else {                    // 从缓存中获取 Service 对象                    service = cache.get(mContextCacheIndex);                    if (service != null) {                        return service;                    }                }                service = createService(ctx);                cache.set(mContextCacheIndex, service);                return service;            }        }        // 子类覆写该方法用以创建服务对象        public Object createService(ContextImpl ctx) {            throw new RuntimeException("Not implemented");        }    }    // 1.Service 容器    private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = new HashMap<>();    private static int sNextPerContextServiceCacheIndex = 0;    // 2.注册服务器    private static void registerService(String serviceName, ServiceFetcher fetcher) {        if (!(fetcher instanceof  StaticServiceFetcher)) {            fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;        }        SYSTEM_SERVICE_MAP.put(serviceName, fetcher);    }    // 3.静态语句块,第一次加载该类时执行,只执行一次,保证实例的唯一性    static {        // 代码省略        // 注册LayoutInflater service        registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {            public Object createService(ContextImpl ctx) {                return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());            }        });        // 代码省略    }    public Object getSystemService(String name) {        // 根据 name 来获取服务        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);        return fetcher == null ? null :fetcher.getService(this);    }    // 代码省略}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

分析可以看出,在虚拟机第一次加载该类时会注册各种 ServiceFetcher,其中就包含了 LayoutInflater Service,将这些服务以键值对的形式存储在一个 HashMap 中,使用时只需要根据 key 获取 对应的 ServiceFetcher,然后通过 ServiceFetcher 对象的 getService() 方法获取具体服务对象。首次调用 ServiceFetcher 的 createService() 创建服务对象并将其缓存进一个列表中,下次直接从缓存中获取,避免重复创造对象,从而达到单例效果。系统核心服务以单例形式存在,减少了资源消耗。

0 0
原创粉丝点击