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 ...
随机推荐
- lxml包引入错误
在使用第三方包lxml引入etree模块时报错: >>> from lxml import etree Traceback (most recent call last): File ...
- How do I fix a “Unknown configuration key `foreign-architecture' found in your `dpkg' configuration files.” error?
My /etc/dpkg/dpkg.cfg.d/multiarch contained: foreign-architecture i386 I deleted the file. I then is ...
- Centos 7 文件和目录管理
查看权限在终端输入: ls -l xxx.xxx (xxx.xxx是文件名) 那么就会出现相类似的信息,主要都是这些: -rw-rw-r-- 其中: 最前面那个 - 代表的是类型 中 ...
- java LimitedThreadPool
此线程池一直增长,直到上限,增长后不收缩(因为池子里面的线程是永生的).这个keepAliveTime参数设置的为Long.MAX_VALUE,所以池子里面的线程几乎不会因为idle而被termina ...
- 【DB2】索引
1. 索引的概念 1.1 索引的优点 索引是表的一个或多个列的键值的有序列表,创建索引的原因有2个: 确保一个或多个列中值的唯一性 提高对表进行的查询的性能.当查询时想要以更快的速度找到所需要的列,或 ...
- Npoi导出excel整理(附源码)
前些日子做了一个简单的winform程序,需要导出的功能,刚开始省事直接使用微软的组件,但是导出之后发现效率极其低下,绝对像web那样使用npoi组件,因此简单的进行了整理,包括直接根据DataTab ...
- Spring Aspect实现AOP切面
百度搜索的大部分的文章(demo)都只是对简单的方法进行切面(例如:public String say(String name)),却未介绍在入参是不固定的时候改怎么处理,后来查到可以使用org.as ...
- windows 10 更新失败及应用商店重装问题解决记录
简单的记录一下这次遇到的问题及解决办法. 使用的windows 10 企业版一直不能更新成功,各种办法都试过了,都是失败然后回退. 这次直接下载了1709的映像进行升级安装的,因为我的是双系统,升级安 ...
- 查看Window系列本地账户密码
mimikatz,很出名的查看Window本地账户密码(经测试,不支持探测Window在线账户认证密码的探测) github: https://github.com/gentilkiwi/mimika ...
- Log4j 把不同包的日志打印到不同位置
如果需要将不同的日志打印到不同的地方,则需要定义不同的Appender,然后定义每一个 Appender的日志级别.打印形式.输出位置! 配置log4j.properties文件如下: ####### ...