欢迎光临散文网 会员登陆 & 注册

Android设计模式(一)单例模式

2022-02-08 22:24 作者:房顶上的铝皮水塔  | 我要投稿

参考内容:《Android源码设计模式与实战》

android-sdk version:sdk29

代码仓库:https://github.com/kolibreath/Practices

单例模式主要需要满足以下几个特点:

  1. 具有私有的构造方法

  2. 需要满足在多线程环境中单例的一致性

  3. 避免在反序列化时创建新的对象

单例模式有很多种写法,我将它大体上分成三种:

  1. 使用锁机制实现:主要是满足在多线程条件中单例的一致性

  2. 使用类加载机制:static关键字在其中扮演了重要的作用

  3. 特殊的形式:比如以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等。

Android设计模式(一)单例模式的评论 (共 条)

分享到微博请遵守国家法律