android笔记之SystemServiceRegistry

前言

    最近在看android源码中Window的内容,在看到WindowManagerImpl创建对象时发现了SystemServiceRegistry,这个类主要是用来缓存、注册、获取系统服务的。因为对它的缓存机制比较感兴趣,所以就研究下它的源码并记录下来,以便以后回顾迭代。

1. SystemServiceRegistry刚被导入内存时的分析

    先来看一段源码:

package android.app;
final class SystemServiceRegistry {

    //用来保存所有Service的名字
    private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
            new HashMap<Class<?>, String>();
    //用来保存所有的ServiceFetcher,后面会拿出ServiceFetcher源码来说
    //现在只要知道它是从缓存中拿Service,缓存中没有就创建Service就可以了
    private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new HashMap<String, ServiceFetcher<?>>();
    //记录缓存的大小
    private static int sServiceCacheSize;
    
    //注意构造方法是private的
    private SystemServiceRegistry() { }
    
    static{
        
        //注册服务代码
        registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
                new CachedServiceFetcher<AccessibilityManager>() {
            @Override
            public AccessibilityManager createService(ContextImpl ctx) {
                return AccessibilityManager.getInstance(ctx);
            }});
        
        //注册服务代码
        registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
                new CachedServiceFetcher<CaptioningManager>() {
            @Override
            public CaptioningManager createService(ContextImpl ctx) {
                return new CaptioningManager(ctx);
            }});
        ...//大量类似的注册服务代码
    }
}

    在SystemServiceRegistry类刚被导入内存时,这些静态字段就会被初始化、静态字段就会被执行。SYSTEM_SERVICE_NAMES、SYSTEM_SERVICE_FETCHERS这两个字段不仅是静态的,还是final的,这说明这俩字段被初始化之后,对象就不能更改了但可以增删元素(因为是HashMap)。这两个字段,分别用来存储Service名字和获取Service实例的Fetcher对象的。

    SystemServiceRegistry的构造方法是私有的,说明它不能在类外创建对象,看一下该构造方法在类内的调用,发现也没有调用,这说明SystemServiceRegistry是不允许创建对象的。再看一下该类的其它方法和字段,发现都是静态的,这就可以确定SystemServiceRegistry类是被当做Util来使用的,并且不允许创建该类的实例。

    static代码块中大量调用了registerService方法,那就结合registerService方法的调用和声明来看一下:

...
static{
    ...
    //registerService的调用
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
    ...
}

...
//registerService的声明
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);
}
...

    先看一下registerService方法声明的参数:

参数 含义
serviceName service的名字,比如Context.WINDOW_SERVICE
serviceClass 该Service对象的Class类,比如WindowManager.class
serviceFetcher ServiceFetcher对象

    registerService方法内的逻辑很简单,就是以serviceClass为key将serviceName存储到SYSTEM_SERVICE_NAMES这个HashMap中,以serviceName为key将ServiceFetcher对象对象存储到SYSTEM_SERVICE_FETCHERS这个HaspMap中。

    下面看一下,ServiceFetcher的声明及其实现类CachedServiceFetcher。如上述代码所示,registerService方法被调用时传入的第三个参数即为CachedServiceFetcher的对象。

package android.app;
final class SystemServiceRegistry {
    ...
    private static int sServiceCacheSize;
    ...
    static abstract interface ServiceFetcher<T> {
        T getService(ContextImpl ctx);
    }
    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) {
                // Fetch or create the service.
                Object service = cache[mCacheIndex];
                if (service == null) {
                    service = createService(ctx);
                    cache[mCacheIndex] = service;
                }
                return (T)service;
            }
        }

        public abstract T createService(ContextImpl ctx);
    }
    ...
}

    从代码结构来看,ServiceFetcher为SystemServiceRegistry类的静态内部接口,定义了getService方法;CachedServiceFetcher为SystemServiceRegistry类的抽象静态内部类,实现ServiceFetcher接口并实现其定义方法getService方法。

    另外,CachedServiceFetcher的构造方法被调用时会将此时SystemServiceRegistry类中静态成员变量sServiceCacheSize的值赋给其final的成员变量mCacheIndex,然后sServiceCacheSize加1。结合上述static代码块中大量调用registerService方法时大量new CachedServiceFetcher对象作为第三个参数可知,SystemServiceRegistry类中静态成员变量sServiceCacheSize其实就是用来记录static代码块中注册了多少个服务的。同时还保证了每一个注册的服务都有一个唯一的下标mCacheIndex(与其他注册服务的下标不同)记录在ServiceFetcher对象内。

说一个小知识点:类的成员final非静态变量如果未初始化,那其实是可以在该类的构造方法初始化的。比如说,这里的mCacheIndex。

    SystemServiceRegistry类被导入内存时,能执行到的差不多就是如上所说了:静态成员变量的初始化,静态代码块以及静态代码块中执行到的registerService方法和给registerService传递参数时执行到的CachedServiceFetcher类的构造方法。

    SystemServiceRegistry类一般只会导入内存一次,然后就留在内存里了。这样就保证了static代码块只会执行一次,注册各个服务也只各执行一次,获取服务的ServiceFetcher对象也是一个服务对应一个。

2. 使用SystemServiceRegistry提供的方法创建缓存并获取服务

    通过上述对registerService方法功能的分析可知,SystemServiceRegistry存储下来的只是service名字和获取该service的ServiceFetcher对象,并没有直接存储该Service实现类的对象。那SystemServiceRegistry的缓存是在什么时候创建的?Service实现类的对象又是什么创建并存入到缓存的呢?

    1. 创建缓存

    SystemServiceRegistry类的方法并不多,public修饰可被外界调用的也只有3个而已:createServiceCache、getSystemService、getSystemServiceName。根据方法名就很快找到了创建缓存的方法createServiceCache,下面具体看一下这个方法的声明:

public static Object[] createServiceCache() {
    return new Object[sServiceCacheSize];
}

    超级简单的一个方法,就创建了一个Object数组而已。上面已经解释了sServiceCacheSize的作用是记录注册的所有服务的个数,而此处使用sServiceCacheSize来作为缓存数组的长度。

    关于创建缓存还有一点要说的,查看createServiceCache方法被调用的地方,发现它只在初始化ContextImpl的一个成员变量时被调用了,其它地方没调用:

package android.app;
class ContextImpl extends Context {
    ...
    final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
    ...
}

    从这里就可以看出,SystemServiceRegistry类中的缓存对象并没有设置到SystemServiceRegistry类中,而是存储到了另一个类ContextImpl的成员变量上了。这个跟ThreadLocal中的静态内部类ThreadLocalMap的实例对象并没有设置在ThreadLocal类中,而是设置在了Thread类的成员变量上多多少少有些相似。

    2. 获取Service实例

    上面说了,SystemServiceRegistry中可被外界调用的public方法很少,只有3个。上面说了用来创建缓存数组的createServiceCache方法,下面说一下用来获取Service实例的getSystemService方法:

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

    也是超级简单的一个方法,从SystemServiceRegistry的静态且final的hashmap(SYSTEM_SERVICE_FETCHERS)中取出ServiceFetcher对象(ServiceFetcher对象在SystemServiceRegistry类被导入内存执行static代码块时已被初始化),然后调用其getService方法来获取Service实例。下面来看一下ServiceFetcher的实现类CachedServiceFetcher中的getService方法(CachedServiceFetcher类上面已经贴过,这里只贴出其方法getService):

@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
    //从ContextImpl对象中获得缓存数组
    final Object[] cache = ctx.mServiceCache;
    //某个线程正在获取Service,其他获取Service线程在这里阻塞
    synchronized (cache) {
        //一个下标对应一个Service,从缓存数组中获取该下标下的Service
        Object service = cache[mCacheIndex];
        //如果缓存数组中还没有Service就要去创建,创建好后保存到缓存数组的相应下标下
        if (service == null) {
            service = createService(ctx);
            cache[mCacheIndex] = service;
        }
        //返回该服务
        return (T)service;
    }
}

public abstract T createService(ContextImpl ctx);

    这个getService的逻辑基本就是缓存数组中获取不到该服务就先去创建该服务,创建好后将该服务存储到缓存数组中并返回该服务。下面看一下CachedServiceFetcher类中的createService方法:

static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
    ...
    public abstract T createService(ContextImpl ctx);
}

    该方法是抽象的,那该方法的具体实现在哪儿呢?其实在上述的static代码中new CachedServiceFetcher对象时在后面的大括号中已经实现了createService方法。比如静态代码块中注册窗口服务时是这样的:

registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
    @Override
    public WindowManager createService(ContextImpl ctx) {
        return new WindowManagerImpl(ctx);
    }});

    到此已经通过获取了Service实例。

总结

    这里作一个总结,以便对SystemServiceRegistry有一个整体的印象:

  • SystemServiceRegistry类中ServiceFetcher对象是复用的,不同的ContextImpl对象获取相同的Service实例使用的同一个ServiceFetcher对象;
  • 对Service缓存的数组是记录在ContextImpl中的,不同的ContextImpl对象缓存数组不同。
  • Service缓存数组初始大小即为要注册的所有服务的个数,但数组每个位置上的Service实例均为空。只有在调用SystemServiceRegistry的getSystemService方法时发现没这个Service实例才会去创建这个Service实例,并存储到缓存数组中,保证再次调用getSystemService方法时返回缓存数组中的该实例;
  • SystemServiceRegistry这种缓存机制的好处在于,不同ContextImpl对象使用不同的缓存数组,但不同的缓存数组获取相同的Service实例可以复用同一个ServiceFetcher对象;只在需要获取Service实例时才会去创建该Service实例,并保证以后需要时返回缓存中的该实例。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,565评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,170评论 11 349
  • 我好像给一些人说过,你一定要幸福。 你一定要幸福,基于的初衷很简单。人物:你。事件:幸福。 其实我有很多话没有说。...
    nic7eee阅读 347评论 0 0
  • 今天是我的第一次产品嘉年华,结合了环保超市的产品,中国茶文化和插花艺术,伙伴们都非常喜欢,这样的结果让我更有信心邀...
    珊珊手心阅读 246评论 0 1