Android设计模式(一)单例模式
参考内容:《Android源码设计模式与实战》
android-sdk version:sdk29
代码仓库:https://github.com/kolibreath/Practices

单例模式主要需要满足以下几个特点:
具有私有的构造方法
需要满足在多线程环境中单例的一致性
避免在反序列化时创建新的对象
单例模式有很多种写法,我将它大体上分成三种:
使用锁机制实现:主要是满足在多线程条件中单例的一致性
使用类加载机制:static关键字在其中扮演了重要的作用
特殊的形式:比如以enum、容器形式

使用类加载机制的单例模式
因为类在加载的过程中会初始化其中的static成员,这样在后续获取单例的时候不会重新实例化新的成员;并且这个类加载一定先于多线程调用的环境,所以也可以避免多线程的影响。
使用类加载机制(static关键字)主要的三种单例模式:饿汉式、静态内部类
饿汉式
饿汉式就是在类加载时候马上初始化类成员,立即加载,而不是延迟加载
静态内部类
静态内部类其实不像真正的【内部类】,因为内部的语义不强。静态内部类在类加载的时候不会被初始化,更别说其中定义的静态成员。在getInstance时才会加载静态内部类,这样其中的静态成员也会被延迟加载。相对于饿汉式好一点。
使用锁机制的单例模式
这种方式主要是考虑多线程情况下,并且使用延迟加载的方式。
懒汉式单例模式
懒汉式单例模式使用synchronized保证一个时刻只能有一个线程调用这个方法,这样内部的new Singleton_lazy()一定是原子过程。但是这样的方法很慢,线程需要阻塞排队获取synchronized锁。
DCL单例模式
另外我们可以使用DCL避免在方法上加锁,更细粒度的块中加锁。但是这种方法需要将单例写为violatile变量。因为JVM执行指令可能会重排序,violatile保证new Singleton_DCL()的实例构建,成员初始化对于其他线程都是可见的。
其他形式
使用枚举
利用枚举是天生线程安全的特性
使用容器
比如可以启用一个Hashmap,如果没有初始化,就将这个变量放入HashMap中。
我们通过Android sdk中LayoutInflater的初始化来说明这个问题。

单例模式在LayoutInflater系统服务中的应用
我们通常使用Layout.from获取一个LayoutInflater,然后会调用Context#getSystemService,返回一个LayoutInflater。
这里的Context实际上是ContextImpl,我们来看后续代码:
ContextImpl在后面的过程中会去找到一个静态hash容器,从容器中返回值。存储在Hashmap中的服务只会在SystemServiceRegistry中加载一次:

对于LayoutInflater的补充说明
从上面可以看出,实际上是PhoneLayoutInflater完成的工作。具体我们以一个Activity解析xml的过程简单说明一下问题:
Activity加载页面的代码在setContentView这块,然后我们也知道getWindow返回的是PhoneWindow,我们通过查看PhoneWindoe#setContentView,发现里面和LayoutInflater相关的代码只有如上一处。
LayoutInflater#inflate就是解析xml树的过程,将其中的View进行实例化,最终会调用到LayoutInflater#createViewFromTag:
这里分为对自定义View和android.widget中的View的解析,这两个方法都会调用到createView。在createView中就是通过反射的方式构建View的实例,具体的代码我就不列出来了。值得说明的是creatView中也使用了HashMap缓存Constructor方法。
类似的使用容器同一管理单例的方法还可以再Retrofit中看到,比如管理ServiceMethod等。