本文分析了 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 ,keyStringvalueServiceFetcher<?>
    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 源码解析:单例模式-通过容器实现单例模式-懒加载方式的更多相关文章

  1. spring源码解析之IOC容器(二)------加载和注册

    上一篇跟踪了IOC容器对配置文件的定位,现在我们继续跟踪代码,看看IOC容器是怎么加载和注册配置文件中的信息的.开始之前,首先我们先来了解一下IOC容器所使用的数据结构-------BeanDefin ...

  2. Android源码解析系列

    转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...

  3. android源码解析(十七)-->Activity布局加载流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...

  4. vue+element ui项目总结点(一)select、Cascader级联选择器、encodeURI、decodeURI转码解码、mockjs用法、路由懒加载三种方式

    不多说上代码: <template> <div class="hello"> <h1>{{ msg }}</h1> <p> ...

  5. Android源码解析——LruCache

    我认为在写涉及到数据结构或算法的实现类的源码解析博客时,不应该急于讲它的使用或马上展开对源码的解析,而是要先交待一下这个数据结构或算法的资料,了解它的设计,再从它的设计出发去讲如何实现,最后从实现的角 ...

  6. Android源码解析——Toast

    简介 Toast是一种向用户快速展示少量信息的视图.当它显示时,它会浮在整个应用层的上面,并且不会获取到焦点.它的设计思想是能够向用户展示些信息,但又能尽量不显得唐突.本篇我们来研读一下Toast的源 ...

  7. Android源码解析——AsyncTask

    简介 AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用.它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler.它 ...

  8. Spring5源码解析_IOC之容器的基本实现

    前言: 在分析源码之前,我们简单回顾一下SPring核心功能的简单使用: 容器的基本用法 Bean是Spring最核心的东西,Spring就像是一个大水桶,而Bean就是水桶中的水,水桶脱离了水就没有 ...

  9. 【Spring源码分析系列】启动component-scan类扫描加载过程

    原文地址:http://blog.csdn.net/xieyuooo/article/details/9089441/ 在spring 3.0以上大家都一般会配置一个Servelet,如下所示: &l ...

随机推荐

  1. java Map常用方法封装

      java Map常用方法封装 CreationTime--2018年7月16日15点59分 Author:Marydon 1.准备工作 import java.util.HashMap; impo ...

  2. 什么是XML RPC?

    # -*- coding: cp936 -*- #python 27 #xiaodeng #什么是XML RPC? #中文叫:远程过程调用 #使用http协议做传输协议的rpc机制,使用xml文本的方 ...

  3. 触发JVM进行Full GC的情况及应对策略

    堆内存划分为 Eden.Survivor 和 Tenured/Old 空间,如下图所示: 从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,对老年代GC称为M ...

  4. jenkins+maven+junit构建自动化测试,整合junit xml生成直观的测试报告[留存]

    在自动化测试过程中,测试报告最能直观的体现测试的价值,之前一直使用maven+junit来构建我的自动化测试,但这样有几个缺点,一是,不能定时构建自动化任务(也许是我没有找到maven有没有提供这样的 ...

  5. 【转】IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段

    背景知识 IP地址 IP地址被用来当做Internet上的电脑的身份编号.大家日常见到的情况是每台联网的PC上都需要有IP地址,才能正常通信.我们可以把“个人电脑”比作“一台电话”,那么“IP地址”就 ...

  6. 9273:PKU2506Tiling

    9273:PKU2506Tiling 来源:http://noi.openjudge.cn/ch0202/9273/ 总时间限制:2000ms  单个测试点时间限制:1000ms内存限制:131072 ...

  7. Swift3 CADisplayLink简单用法

    1.定义属性 var displayLinkTimer:CADisplayLink? = nil 2.init displayLinkTimer = CADisplayLink(target: sel ...

  8. SaltStack入门到精通第一篇:安装SaltStack

    SaltStack入门到精通第一篇:安装SaltStack 作者:纳米龙  发布日期:2014-06-09 17:50:36   实际环境的设定: 系统环境: centos6 或centos5 实验机 ...

  9. No package的问题解决

    更新pecl扩展 yum  install epel-release  //扩展包更新包yum  update //更新yum源

  10. 微信公众平台HTTPS方式调用配置免费https服务器

    微信公众平台数据传输安全,提高业务安全性,公众平台将不再支持HTTP方式调用.避免影响正常使用中含有HTTP方式调用的服务,请开发者尽快调整,将现有通过HTTP方式调用的切换成HTTPS调用,平台将于 ...