作为一个Android开发的老司机,或者刚入行的司机,我觉得你还是有必要学习下Android的单例模式,毕竟
单例模式是我们很常用的一个设计模式。
1. 介绍
1.1 定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
实现单例主要有如下几个关键点:
- 构造方法不对外开放,一般是private,防止外部实例化。
- 通过一个静态方法返回实例对象。
- 保证实例只有一个,尤其是多线程环境下。
- 确保单例对象在反序列化的情况下,不会重新构建对象。
1.2 单例模式的几种实现方式
单例模式的写法真的太多了,但是如果你读过Effective Java这本书的话,里面着重推荐的就是使用枚举的方式来实现单例模式。
我们还是要总结下单例的实现方式:
- 饿汉模式(多线程安全,需要处理反序列化问题)
- 饱汉模式(多线程不安全、需要处理反序列化问题)
- DCL(高并发会出现DCL检查失效问题,1.6之前使用volatile修饰实例变量)
- 静态内部类(多线程安全,需要处理反序列化问题)
- 枚举(简单、推荐、防止反序列化)
- 使用容器实现单例模式
具体的写法我们不做多的介绍了,要是感兴趣可以自行查询。
2. Android中的单例模式
首先明确一点,Android中的单例模式绝大部分采用了[容器]来实现。
使用容器实现单例模式,主要有以下特点:
- 在程序初始化时,将多种单例注入到一个统一的管理类中。
- 使用时根据key获取单例对象。
- 我们可以管理多种类型的单例,并且在需要时通过一个统一的接口来进行获取。
- 对用户隐藏了具体的实现,降低了耦合度。
好了,带着这个先入为主的概念,接下来我来带你分析下具体的实现。
在日常开发中,我们经常使用系统提供的服务,如WindowManagerService,PackageManagerService,ActivityManagerService等,但是我们最经常使用的一定少不了LayoutInflater,这些服务会在合适的时候以单例的形式注册在系统中,我们需要的时候只需要通过context.getSystemService(String serviceName)方法获取即可。
我们本章节以LayoutInflater为例,来一探Android源码中单例模式究竟。
2.1 LayoutInflater获取
我们首先来回顾下LayoutInflater的使用方式:
// 1. 调用LayoutInflater.from方法
LayoutInflater inflater = LayoutInflater.from(this);
// 2. 调用context.getSystemService获取,并转换
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// LayoutInflater.from
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;
}
代码示例中,我们列出了我们经常使用的获取LayoutInflater的方法,如果你翻看一下LayoutInflater.from的源码,你会惊奇的发现LayoutInflater.from是对我们经常使用的第二种方式的封装。
看来LayoutInflater.from只是系统方便我们使用简化的方法,你看Android的设计者多为我们考虑啊。
根据这个思路,我们甚至可以封装一个工具类来获取我们常用的服务类,而不用再写一大坨获取并强制转换的代码。
2.2 getSystemService
经过上面的分析,我们知道我们常用的服务类,是通过调用context.getSystemService来获取的。
追溯到cotext的源码,我们发现Context只是一个接口,而不是具体的实现类,而它的具体实现类是ContextImpl,我们看下getSystemService的源码。
// ContextImpl用来缓存已经创建的服务实例
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
@Override
public Object getSystemService(String name) {
// 原来是调用了SystemServiceRegistry类的静态方法
return SystemServiceRegistry.getSystemService(this, name);
}
ContextImpl也并未存储具体的单例对象,调用了SystemServiceRegistry.getSystemService获取并返回实例。
2.3 SystemServiceRegistry
为了不影响我们的分析视线,我摘取了SystemServiceRegistry的核心代码,代码如下:
final class SystemServiceRegistry {
// 你要的容器类在这里
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
// 通过该方法来获取service
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
// 这个是注册服务的方法
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
static {
// 我们使用的ActivityManager在这注册
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
// 我们使用的AlarmManager在这注册
registerService(Context.ALARM_SERVICE, AlarmManager.class,
new CachedServiceFetcher<AlarmManager>() {
@Override
public AlarmManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.ALARM_SERVICE);
IAlarmManager service = IAlarmManager.Stub.asInterface(b);
return new AlarmManager(service, ctx);
}});
// 终于找到你,LayoutInflater就是在这注册的
// 现在我们还知道LayoutInflater的实现类为PhoneLayoutInflater
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
}
}
getSystemService并不是在HashMap中直接存储服务实例,而是存储的ServiceFetcher。我们看看下ServiceFetcher的源码。
static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}
我们看到ServiceFetcher只是一个接口,它定义通过ContextImpl获取实实例。
我们看一个ServiceFetcher的具体实现类CachedServiceFetcher,也是我们上面分析的代码中使用的类。
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
private final int mCacheIndex;
public CachedServiceFetcher() {
// 记录当前服务实例(T)所对应的缓存Index
mCacheIndex = sServiceCacheSize++;
}
@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
// 获取ContextImpl中的缓存数组
final Object[] cache = ctx.mServiceCache;
synchronized (cache) {
// Fetch or create the service.
// 从ContextImpl中的缓存数组中获取服务实例,存在直接返回,不存在则创建并加入缓存数组中
Object service = cache[mCacheIndex];
if (service == null) {
try {
service = createService(ctx);
cache[mCacheIndex] = service;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return (T)service;
}
}
public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
分析下getService流程如下:
- 首先从ContextImpl缓存数组中获取对应的实例。
- 如果实例存在,直接返回实例对象;否则创建实例,并加入ContextImpl缓存数组中。
CachedServiceFetcher的实现了保证了,实例对象只在首次获取时才会被初始化,否则就返回已经缓存的对象,节省了系统资源。
2.4 综合分析
综合分析我们得出如下结论:
- 我们常用的一些系统Service是通过SystemServiceRegistry进行管理的。
- 系统加载SystemServiceRegistry类时,通过静态块将各种服务类存储在SYSTEM_SERVICE_FETCHERS(HashMap<ServiceName,ServiceFether>)中,每一个具体的服务类都对应一个ServiceFether。
- 使用时从HashMap中获取ServiceFetcher,并从中获取需要的实例,获取的实例被缓存在ContextImpl中备用。
- CachedServiceFetcher负责创建具体的实例,并将实例缓存到ContextImpl的缓存数组中。
涉及单例模式的几个要点:
- SystemServiceRegistry使用HashMap管理ServiceFether。
- ServiceFether保证实例只创建一次,且通过使用synchronized机制保证多线程安全。
- 加入缓存机制,保证实例只会被创建一次。
3. 总结
单例模式经常出现在我们的日常开发中,我们对于常规的写法(getInstance)应该都十分熟悉了,那么了解下Android系统的单例实现也算是拓展了一种思路吧。