Android中的单例模式

作者: Gzw丶南山 | 来源:发表于2017-03-27 15:12 被阅读66次

今天不思考人生,说说 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 中的系统服务虽然是也是单例,但是分上下文的单例和应用内的单例,虽然这是一个非常非常小的一个点,很有趣是不是,很多知识都是通过好奇心驱动去获取的。

相关文章

  • LayoutInflater源码分析

    在《(-)Android中的单例模式》分析中,我们分析了Android中单例模式的实现,且以LayoutInfla...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • JAVA基础之单例

    JAVA单例的几种形式以及其优缺点。 Android 中的单例模式 - 简书 单例的定义:Singleton模式的...

  • Android 架构师之路5 设计模式之单例模式

    Android 架构师之路 目录 前言 Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式...

  • 简单聊聊单例模式

    单例模式应该是Android开发中常用的一种设计模式。不仅我们经常用到,Android源码中也经常可以看到单例模式...

  • Android 单例模式学习

    android的单例模式学习 1 饿汉单例模式 优点 : 缺点 : 2.0 懒汉模式 优点 : 缺点 : 2.1 ...

  • 11.1设计模式-单例模式-详解

    单例模式: 单例介绍 单例的六种写法和各自特点饿汉、懒汉、懒汉安全、DCL、静态内部类、枚举。 android中的...

  • Android 设计模式之简单工厂模式

    设计模式系列文章 Android 设计模式之单例模式 Android 设计模式之Builder模式 Android...

  • Android 中的单例模式

    Android 中的单例模式 概述 单例模式算是我接触设计模式这种思想所学习的第一个设计模式。记得刚入行时面试,面...

  • Android 中的 23 种设计模式

    系列笔记 通过学习 Android 的设计模式而记的笔记。(持续更新中...) 源码地址 Android 单例模式...

网友评论

    本文标题:Android中的单例模式

    本文链接:https://www.haomeiwen.com/subject/szrfottx.html