Android 源码解析:单例模式-通过容器实现单例模式-懒加载方式
本文分析了 Android 系统服务通过容器实现单例,确保系统服务的全局唯一。
- 开发过 Android 的用户肯定都用过这句代码,主要作用是把布局文件 XML 加载到系统中,转换为 Android 的 View:
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- 通过以上代码可以拿到 layoutflater 的对象, 然后通过 layoutInflater 就可以把 xml 文件转换为 View 加载到 activity 中,然后操作布局了,那么这个对象是怎么拿到的呢,跟源码。
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;
}
- 可以看到调用了很常见的一句代码:
context.getSystemService(), 这句代码应该都用过,调用该方法,传入一个Context的常量,可以拿到相对于的Manager的对象 - 经常见到的常量:
- WINDOW_SERVICE: 获取 WindowManager 实例
- LAYOUT_INFLATER_SERVICE: 获取 LayoutInflater 实例
- ACTIVITY_SERVICE: 获取 ActivityManager 实例
- ALARM_SERVICE: 获取 AlarmManager 实例
- ...
- 跟进
Context类的这个方法,Context是个抽象类,该方法是个抽象方法,源码如下:
public abstract class Context {
...
public abstract Object getSystemService(@ServiceName @NonNull String name);
...
}
Context类的实现类是ContextImpl, 进入并查找这个方法,源码如下:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
- 可以看到进入到了
SystemServiceRegistry类,跟进看源码:
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
SYSTEM_SERVICE_FETCHERS是一个HashMap,key是String,value是ServiceFetcher<?>
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
- ServiceFetcher<?> 是一个接口, 有一个方法,是获取 Service
static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}
- SystemServiceRegistry 源码
// 该类设计成 final 类,防止被继承了。
final class SystemServiceRegistry {
private final static String TAG = "SystemServiceRegistry";
// 注册的服务信息,存入到两个 HashMap 中。
// 两个 HashMap 都是设置为静态的,初始化以后不会再变化的。
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
new HashMap<Class<?>, String>();
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
private static int sServiceCacheSize;
// 私有构造方法
private SystemServiceRegistry() { }
// 静态代码块,第一次类加载的时候加载,且只加载一次,保证实例唯一
static {
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
// createService 该方法只会调用一次,在缓存数组中没有的情况下才会调用,剩下都是从缓存数组中获取的。
@Override
public AccessibilityManager createService(ContextImpl ctx) {
// 调用每个 Manager 的初始化方法
return AccessibilityManager.getInstance(ctx);
}});
registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
new CachedServiceFetcher<CaptioningManager>() {
@Override
public CaptioningManager createService(ContextImpl ctx) {
return new CaptioningManager(ctx);
}});
registerService(Context.ACCOUNT_SERVICE, AccountManager.class,
new CachedServiceFetcher<AccountManager>() {
@Override
public AccountManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.ACCOUNT_SERVICE);
IAccountManager service = IAccountManager.Stub.asInterface(b);
return new AccountManager(ctx, service);
}});
// 此处省略了N多的 registerService() 方法
...
}
/**
* 创建一个数组做缓存,把创建的 service 实例放入到该数组缓存起来
* 该方法在
*/
public static Object[] createServiceCache() {
return new Object[sServiceCacheSize];
}
/**
* 根据常量或者字符串获取指定服务
*/
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
/**
* 通过 service 类 获取指定系统服务的名称,(这个方法好像没有用过)
*/
public static String getSystemServiceName(Class<?> serviceClass) {
return SYSTEM_SERVICE_NAMES.get(serviceClass);
}
/**
* 在静态代码块中会调用到,添加 serviceName 和 serviceClass 到两个 HashMap 中。
*/
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);
}
/**
* Base interface for classes that fetch services.
* These objects must only be created during static initialization.
*/
static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}
/**
* 带有缓存的获取 service 抽象类,只要需要加载 service 的时候才初* 始化,相当于实现了单例模式的懒加载方式
*/
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
private final int mCacheIndex;
public CachedServiceFetcher() {
mCacheIndex = sServiceCacheSize++;
}
@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
//
final Object[] cache = ctx.mServiceCache;
synchronized (cache) {
// 查找缓存,没有缓存的话,调用 createService() 初始化一个相关的 service
Object service = cache[mCacheIndex];
if (service == null) {
service = createService(ctx);
cache[mCacheIndex] = service;
}
return (T)service;
}
}
// 实现类需要重写该方法,针对不同的 service 实现不同
public abstract T createService(ContextImpl ctx);
}
...
}
- 该类上来就是一个静态代码块,在该类加载的时候,初始化了这个代码块,代码块中调用了静态方法
registerService(), 把各个service类通过键值对的形式存储到两个HashMap中,用户使用的时候,通过key获取到HashMap中的ServiceFetcher, 然后调用 ServiceFetcher 的 getService() 方法获取到实例。该方法中,先从数组缓存中查找是否有实例,没有的话,调用 createService 方法,初始化该 service 类,并把该类实例放入到数组缓存中。下次用户再用的时候,会直接返回上次的唯一实例,实现了 Android 系统服务的单例模式。
思考
- 针对项目的底层服务,需要提供大量单例的,可以采用容器和缓存的方式实现单例模式懒加载方式。通过该方式对项目进行重构和优化。
Android 源码解析:单例模式-通过容器实现单例模式-懒加载方式的更多相关文章
- spring源码解析之IOC容器(二)------加载和注册
上一篇跟踪了IOC容器对配置文件的定位,现在我们继续跟踪代码,看看IOC容器是怎么加载和注册配置文件中的信息的.开始之前,首先我们先来了解一下IOC容器所使用的数据结构-------BeanDefin ...
- Android源码解析系列
转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...
- android源码解析(十七)-->Activity布局加载流程
版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...
- vue+element ui项目总结点(一)select、Cascader级联选择器、encodeURI、decodeURI转码解码、mockjs用法、路由懒加载三种方式
不多说上代码: <template> <div class="hello"> <h1>{{ msg }}</h1> <p> ...
- Android源码解析——LruCache
我认为在写涉及到数据结构或算法的实现类的源码解析博客时,不应该急于讲它的使用或马上展开对源码的解析,而是要先交待一下这个数据结构或算法的资料,了解它的设计,再从它的设计出发去讲如何实现,最后从实现的角 ...
- Android源码解析——Toast
简介 Toast是一种向用户快速展示少量信息的视图.当它显示时,它会浮在整个应用层的上面,并且不会获取到焦点.它的设计思想是能够向用户展示些信息,但又能尽量不显得唐突.本篇我们来研读一下Toast的源 ...
- Android源码解析——AsyncTask
简介 AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用.它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler.它 ...
- Spring5源码解析_IOC之容器的基本实现
前言: 在分析源码之前,我们简单回顾一下SPring核心功能的简单使用: 容器的基本用法 Bean是Spring最核心的东西,Spring就像是一个大水桶,而Bean就是水桶中的水,水桶脱离了水就没有 ...
- 【Spring源码分析系列】启动component-scan类扫描加载过程
原文地址:http://blog.csdn.net/xieyuooo/article/details/9089441/ 在spring 3.0以上大家都一般会配置一个Servelet,如下所示: &l ...
随机推荐
- Python实现微信刷卡支付(条码支付)MicroPay
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/7686765.html 一:资料阅读 场景介绍:https://pay.weixin.qq.com/wiki/d ...
- OpenCV在Windows平台下的源代码编译
OpenCV库官方站点为http://opencv.org/. 在该站点能够下载最新的OpenCV for Windows,比如当前我下载的是opencv-2.4.9.exe ,双击该文件解压到某个文 ...
- git 不用clone整个远程仓库,只把特定的commit给fetch下来的方案
一个麻烦点就是,我需要阅读一个大点的开源项目,远程仓库的代码量太庞大了,如果我需要git reset --hard [commit sha1]感兴趣的commit快照,就首先得git clone整个远 ...
- poj 2059 单调栈
题意:求柱状图中最大矩形面积. 单调栈:顾名思义就是栈内元素单调递增的栈. 每次插入数据来维护这个栈,假设当前须要插入的数据小于栈顶的元素,那就一直弹出栈顶的元素.直到满足当前须要插入的元素大于栈顶元 ...
- 设置mysql group_concat长度
#在MySQL配置文件(my.ini)中默认无该配置项,使用默认值时,值为1024,可在客户端执行下列语句修改: #SET GLOBAL group_concat_max_len = 1024; #该 ...
- SQL SERVER 2008 “阻止保存要求重新创建表的更改”
在SQL SERVER2008中,新建数据表以后,若再对该表进行更改,则会出现警告信息“不允许保存更改 阻止保存要求重新创建表的更改”,等等,需要进行一下设置: 工具--->选项--->D ...
- ios中uiview 转场动画
//转场动画1--头尾 -(void)TransitionAnimation1{ [UIView beginAnimations:nil context:nil]; [UIView setAnimat ...
- ubuntu下安装迅雷thunder
迅雷是windows xp下必装的下载工具,作为一款跨协议的下载软件,迅雷的下载速度极其强悍. 那么在ubuntu下能否安装迅雷呢? 到ubuntu中文论坛逛了一圈,发现有现成的wine-thunde ...
- 由m种数字组成的n位数有多少个
知乎链接 问题描述 我和我女朋友的QQ号都是九位数字,这九个数字是有七个不同的数字组成的,我想问这种概率是多大,我们是不是特别我看缘分呢?求大神给算一下概率! 思路 定义问题:由7种数字组成的9位数一 ...
- 基于 CADisplayLink 的 FPS 指示器详解
前言 之前在开发中有使用到计时器NSTimer,后来了解到iOS中不同的计时方法,其中就包括了CADisplayLink.基于CADisplayLink以屏幕刷新频率同步绘图的特性,尝试根据这点去实现 ...