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 ...
随机推荐
- java Map常用方法封装
java Map常用方法封装 CreationTime--2018年7月16日15点59分 Author:Marydon 1.准备工作 import java.util.HashMap; impo ...
- 什么是XML RPC?
# -*- coding: cp936 -*- #python 27 #xiaodeng #什么是XML RPC? #中文叫:远程过程调用 #使用http协议做传输协议的rpc机制,使用xml文本的方 ...
- 触发JVM进行Full GC的情况及应对策略
堆内存划分为 Eden.Survivor 和 Tenured/Old 空间,如下图所示: 从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,对老年代GC称为M ...
- jenkins+maven+junit构建自动化测试,整合junit xml生成直观的测试报告[留存]
在自动化测试过程中,测试报告最能直观的体现测试的价值,之前一直使用maven+junit来构建我的自动化测试,但这样有几个缺点,一是,不能定时构建自动化任务(也许是我没有找到maven有没有提供这样的 ...
- 【转】IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段
背景知识 IP地址 IP地址被用来当做Internet上的电脑的身份编号.大家日常见到的情况是每台联网的PC上都需要有IP地址,才能正常通信.我们可以把“个人电脑”比作“一台电话”,那么“IP地址”就 ...
- 9273:PKU2506Tiling
9273:PKU2506Tiling 来源:http://noi.openjudge.cn/ch0202/9273/ 总时间限制:2000ms 单个测试点时间限制:1000ms内存限制:131072 ...
- Swift3 CADisplayLink简单用法
1.定义属性 var displayLinkTimer:CADisplayLink? = nil 2.init displayLinkTimer = CADisplayLink(target: sel ...
- SaltStack入门到精通第一篇:安装SaltStack
SaltStack入门到精通第一篇:安装SaltStack 作者:纳米龙 发布日期:2014-06-09 17:50:36 实际环境的设定: 系统环境: centos6 或centos5 实验机 ...
- No package的问题解决
更新pecl扩展 yum install epel-release //扩展包更新包yum update //更新yum源
- 微信公众平台HTTPS方式调用配置免费https服务器
微信公众平台数据传输安全,提高业务安全性,公众平台将不再支持HTTP方式调用.避免影响正常使用中含有HTTP方式调用的服务,请开发者尽快调整,将现有通过HTTP方式调用的切换成HTTPS调用,平台将于 ...