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 ...
随机推荐
- Postgresql学习笔记
一:数据类型 主要有三大类以及其他一些杂项类型: 数值型.字符型.日期型. 数值型: 名称 描述 存储大小 范围 smallint 存储整数,小范围 2字节 -32768 至 +32767 integ ...
- Python学习笔记六:数据库操作
一:Python操作数据库的流程 二:开发环境准备 1:开发工具PyCharm 2:Python操作mysql的工具:需要安装Python-Mysql Connector,网址:https://sou ...
- gzip:stdin:not in gzip format的解决办法
执行解压命令,在解压.gz或者.bz2格式的文件的文件的时候可能会出现这样的错误提示 tar -zxvf rlwrap-0.30.tar.gz 报错如下 gzip: stdin: not in gzi ...
- java byte数组与16进制间的相互转换
java byte数组与16进制间的相互转换 CreationTime--2018年6月11日15点34分 Author:Marydon 1.准备工作 import java.util.Array ...
- pycharm安装提示 module 'pip' has no attribute 'main'
问题描述: 环境: windows10 pycharm2016.2.3 //在最先版本的pycharm就没问题,可能还需要升级pip版本 python3.6 pip安装模块,提示 Attribute ...
- docker-compose 管理多个docker容器实例
Compose 安装 运行此命令下载最新版本的Docker Compose $ curl -L https://github.com/docker/compose/releases/download/ ...
- (原)使用tensorboard显示loss
转载请注明出处: http://www.cnblogs.com/darkknightzh/p/7416551.html 参考网址: http://blog.csdn.net/jerry81333/ar ...
- fisheye Error occurred during initialization of VM Could not reserve enough space for object heap 问题解决!
参考文章:https://answers.atlassian.com/questions/9397/not-enough-heap-space-to-run-fisheye fisheye下载好了之后 ...
- Lua协程学习
按照书上码了下,但运行有问题,暂时不知道原因: function send (x) coroutine.yield(x) end function producer() return coroutin ...
- linux 的时区设置函数tzset() 【转】
linux 的时区设置函数tzset() 本文转载于: http://blog.csdn.net/epicyong333/article/details/5258152 tzset #incude & ...