Android中的单例模式

今天不思考人生,说说 Android 中的单例。

单例模式大概已经被网上写烂了吧?那也拦不住我在写一遍哈哈哈。但今天谈的不是单例怎么写,毕竟如何写单例网上一抓一大把,只是看看 Android 中的单例运用。

先说说单例模式运用场景,为了避免产生多个对象消耗过多的资源,或者某个对象的类型它确实只应该存在一个,那我们就会考虑使用单例模式。

单例模式可用以下方式实现:

  • 饿汉模式
  • 懒汉模式
  • 双重检查
  • 静态内部类
  • 枚举

我在看书中的时候书中说到四个实现单例的关键点:
1.构造函数不对外开放, 一般为 Private;
2.通过一个静态方法或者枚举返回单例类对象;
3.确保单例类的对象有茄子有一个,尤其是在多线程中;
4.确保单例对象在反序列化时不会从构建对象。

1,2毋庸置疑,3,4是需要注意的点,在多线程中我们需要去关心我拿到的单例对象是否依然还是同一个,不过我对于反射这个也有一点的困惑,因为通过反射哪怕是私有修饰符我也可以拿到,从而使我通过私有的构造器去创建另一个该对象的实例(大概是大家不会这么做,而我只是好奇,不过反射你这是作弊啊!!!),对于第四点我不是很懂,我还在继续在网上看资料。。。

//  测试反射创建单例和单例本身
Class<Singleton> singletonClass = Singleton.class;
Constructor[] c = singletonClass.getDeclaredConstructors();
//  可以访问私有
c[0].setAccessible(true);
Singleton singleton = Singleton.getSingleton();
LoggerUtil.printGeneralLog(singleton);
try {
    Singleton s = (Singleton) c[0].newInstance();
    s.test();
    LoggerUtil.printGeneralLog(s);
}...省略catch
通过反射创建.png

今天主要讲的是 Android 中的单例运用,我们最熟悉的应该就是 Android 中的系统服务,它就是通过注册了一系列单例服务供我们使用,那么下面我们就来看看系统服务获取和注册的代码(API 25 的源码)。

我们先来看看如何获取系统服务,在 Context 中提供了几个方法,一个是抽象的方法,通过 Context 定义好的 Sting 去获取服务,getSystemService(@ServiceName @NonNull String name),另一个是不可继承的泛型方法,final <T> T getSystemService(Class<T> serviceClass),它最后会调用 Context 中的抽象方法 String getSystemServiceName(Class<?> serviceClass)。

public final <T> T getSystemService(Class<T> serviceClass) {
        // Because subclasses may override getSystemService(String) we cannot
        // perform a lookup by class alone.  We must first map the class to its
        // service name then invoke the string-based method.
        String serviceName = getSystemServiceName(serviceClass);
        return serviceName != null ? (T)getSystemService(serviceName) : null;
}

因为我们看到 Context 中基本上都是抽象的方法,要去看它实现类 ContextImpl,发现管理着系统服务的类是 SystemServiceRegistry,所有服务都是在static代码块中注册的。

// Service registry information.
// This information is never changed once static initialization has completed.
//  服务会存储在两个Map中一个Class为Key,一个String为key
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<?>>();
//  在静态代码块中注册各种服务
static {
        //  注册方法就是把服务put到上面个Map中
        registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
                new CachedServiceFetcher<AccessibilityManager>() {
            @Override
            public AccessibilityManager createService(ContextImpl ctx) {
                return AccessibilityManager.getInstance(ctx);
            }});
        //  省略其他代码
}

我一开始以为所有系统服务全局都只存在一个,直到某一天和别人讨论后无聊打印了 LayoutInflater 的实例地址,这时候就有一点困惑,系统服务里面的单例是不是真的单例?难道不是全局就只有着一个实例。

LayoutInflater.png

所以我去仔细看了 SystemServiceRegistry 里面的代码,发现了其实它里面其实分成三种类,CachedServiceFetcher 是每个 Context 中不论怎么获取都是同一个实例,而 StaticServiceFetcher 和 StaticApplicationContextServiceFetcher 都是整个应用内的单例。

    /**
     * Override this class when the system service constructor needs a
     * ContextImpl and should be cached and retained by that context.
     */
    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);
    }

    /**
     * Override this class when the system service does not need a ContextImpl
     * and should be cached and retained process-wide.
     */
    static abstract class StaticServiceFetcher<T> implements ServiceFetcher<T> {
        private T mCachedInstance;

        @Override
        public final T getService(ContextImpl unused) {
            synchronized (StaticServiceFetcher.this) {
                if (mCachedInstance == null) {
                    mCachedInstance = createService();
                }
                return mCachedInstance;
            }
        }

        public abstract T createService();
    }

    /**
     * Like StaticServiceFetcher, creates only one instance of the service per application, but when
     * creating the service for the first time, passes it the application context of the creating
     * application.
     *
     * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
     * case where multiple application components each have their own ConnectivityManager object.
     */
    static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
        private T mCachedInstance;

        @Override
        public final T getService(ContextImpl ctx) {
            synchronized (StaticApplicationContextServiceFetcher.this) {
                if (mCachedInstance == null) {
                    Context appContext = ctx.getApplicationContext();
                    // If the application context is null, we're either in the system process or
                    // it's the application context very early in app initialization. In both these
                    // cases, the passed-in ContextImpl will not be freed, so it's safe to pass it
                    // to the service. http://b/27532714 .
                    mCachedInstance = createService(appContext != null ? appContext : ctx);
                }
                return mCachedInstance;
            }
        }

        public abstract T createService(Context applicationContext);
    }

Android 中的系统服务虽然是也是单例,但是分上下文的单例和应用内的单例,虽然这是一个非常非常小的一个点,很有趣是不是,很多知识都是通过好奇心驱动去获取的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容