单例模式以及在android中的使用

Posted 爱炒饭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式以及在android中的使用相关的知识,希望对你有一定的参考价值。

一、原理

程序中某个对象可能比较消耗内存或者创建多个对象实例会引起运行错乱,此时就要求程序中只有一个该对象的实例,也就是单例模式的由来。为了防止开发者创建多个实例,一般会将单例类的构造器设为私有(private),这样你在其它地方去new单例类会失败;然后创建一个该单例类的静态方法去初始化实例对象并返回实例对象,当然实例对象也要是private static的,这样就必须通过静态方法获取该类的实例对象了。考虑到创建对象的过程并不是原子的,也要兼顾多线程安全问题。

二、分类

有多种构建单例的方式,它们都有优缺点和适用场景,下面来列举一下:

2.1 懒汉式

public class LazySingleton {
    private static LazySingleton  mInstance; //实例变量设置私有,防止直接通过类名访问

    private LazySingleton() { //默认构造函数私有,防止类外直接new创建对象
    }

    public static synchronized LazySingleton getInstance() { //静态同步方法作为唯一的实例对象获取方式
        if (mInstance==null) {
            mInstance = new LazySingleton();
        }
        return mInstance;
    }
}

线程安全的懒汉式单例模式的同步锁可以很好的解决多线程问题,并且在访问静态方法时才去创建对象,做到了按需分配,可以在一定程度上节省资源消耗;其实仅在对象第一次初始化时需要保证同步,后面每次访问都去同步就会导致不必要的性能消耗了。

2.2 饿汉式

public class HungrySingleton {
    private static HungrySingleton mInstance = new HungrySingleton();//程序启动时立即创建单例

    private HungrySingleton() {//构造函数私有
    }

    public static HungrySingleton getInstance() {//唯一的访问入口
        return mInstance;
    }
}

由于饿汉式单例模式在程序启动时就去初始化单例变量,等到后面程序其它地方调用静态方法时,单例对象已经初始化完成,所以不存在多线程问题。但是,一上来就去创建对象会消耗系统资源。

2.3 改进的DCL

public class DclSingleton {
    private volatile static DclSingleton  mInstance; //实例变量设置私有,防止直接通过类名访问,volatile禁止指令重排序以及及时通知多线程

    private DclSingleton() { //默认构造函数私有,防止类外直接new创建对象
    }

    public static DclSingleton getInstance() { //唯一的访问入口,访问时才创建对象
        if (mInstance == null) {
            synchronized (DclSingleton.class){
                if (mInstance == null) { //双重检查
                    mInstance = new DclSingleton();
                }
            }
        }
        return mInstance;
    }
}

DCL单例模式可以按需创建对象,第一层判空是为了避免不必要的同步,第二层为空才去创建实例。同时volatile关键字的使用可以保证单例变量的可见性以及禁止指令排序(需要注意的是jdk1.5以后volatile关键字才有这个效果)。

2.4 静态内部类

public class InnerSingleton {
    private InnerSingleton() { //默认构造函数私有,防止类外直接new创建对象
    }
    public static InnerSingleton getInstance() {
        return SingletonHolder.mInstance;
    }
    private static class SingletonHolder {
        private static final InnerSingleton mInstance =  new InnerSingleton();
    }
}

首次加载InnerSingleton类的时候不会加载静态内部类,也就不会初始化mInstance,只有在调用getInstance()方法才会加载SingletonHolder类并初始化mInstance,由于类的初始化期间,JVM会自动获取锁,所以这里不用手动加锁也能保证线程安全。

2.5 枚举

public enum EnumSingleton {
    INSTANCE;
    public void doTest() {

    }
}

默认枚举实例的创建是线程安全的,并且在反序列化时也可以保证是同一个对象。

2.6 容器

public class SingletonManager {
    private static Map<String,Object> objMap = new HashMap<>();

    private SingletonManager() {
    }

    public static void registerService(String key,Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key,instance);
        }
    }

    public static Object getService(String key) {
        return objMap.get(key);
    }
}

该方式类似注册表,将多个单例对象注册到统一的管理类里面,在使用的时候根据key从集合中去取出对应的对象实例,典型代表是android中的getSystemService。

三、android中单例模式的使用

3.1 getSystemService

在2.6中提到了容器单例模式,比较常见的系统服务都是通过这种方式注册,然后应用层通过getSystemService去获取。
新建一个MainActivity中调用下面跟踪一下下面代码

getSystemService(Context.ACCESSIBILITY_SERVICE)

3.1.1 Activiyty.getSystemService

上面代码会调用父类Activiyty.java的getSystemService方法,该方法里面判断不是WINDOW_SERVICE以及SEARCH_SERVICE就去调用父类的getSystemService方法,我们知道Activiyty.java继承ContextThemeWrapper.java,那么就是调用ContextThemeWrapper的getSystemService方法.

//Activiyty.java
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }

    if (WINDOW_SERVICE.equals(name)) {
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    return super.getSystemService(name);
}

3.1.2 ContextThemeWrapper.getSystemService
ContextThemeWrapper的getSystemService方法发现不是LAYOUT_INFLATER_SERVICE的话就会调用getBaseContext()的getSystemService方法。从startService过程 以及从startActivity说起 可以得知getBaseContext()的实现是ContextImpl.java。

//ContextThemeWrapper.java
public Object getSystemService(String name) {
    if (LAYOUT_INFLATER_SERVICE.equals(name)) {
        if (mInflater == null) {
            mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
        }
        return mInflater;
    }
    return getBaseContext().getSystemService(name);
}

3.1.3 ContextImpl.getSystemService
ContextImpl.getSystemService调用了SystemServiceRegistry的getSystemService方法

//ContextImpl.java
public Object getSystemService(String name) {
    if (vmIncorrectContextUseEnabled()) {
        // We may override this API from outer context.
        final boolean isUiContext = isUiContext() || isOuterUiContext();
        // Check incorrect Context usage.
        if (isUiComponent(name) && !isUiContext) {
            final String errorMessage = "Tried to access visual service "
                    + SystemServiceRegistry.getSystemServiceClassName(name)
                    + " from a non-visual Context:" + getOuterContext();
            final String message = "Visual services, such as WindowManager, WallpaperService "
                    + "or LayoutInflater should be accessed from Activity or other visual "
                    + "Context. Use an Activity or a Context created with "
                    + "Context#createWindowContext(int, Bundle), which are adjusted to "
                    + "the configuration and visual bounds of an area on screen.";
            final Exception exception = new IllegalAccessException(errorMessage);
            StrictMode.onIncorrectContextUsed(message, exception);
            Log.e(TAG, errorMessage + " " + message, exception);
        }
    }
    return SystemServiceRegistry.getSystemService(this, name);
}

3.1.4 SystemServiceRegistry.getSystemService
SystemServiceRegistry.getSystemService从SYSTEM_SERVICE_FETCHERS 集合中根据传入的key的名称取出ServiceFetcher,然后从ServiceFetcher取出服务,那么SYSTEM_SERVICE_FETCHERS是什么时候注册的呢?

//SystemServiceRegistry.java
private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
        new ArrayMap<String, ServiceFetcher<?>>();
public static Object getSystemService(ContextImpl ctx, String name) {
    if (name == null) {
        return null;
    }
    final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); //从集合中取出key对应的fetcher 
    if (fetcher == null) {
        if (sEnableServiceNotFoundWtf) {
            Slog.wtf(TAG, "Unknown manager requested: " + name);
        }
        return null;
    }

    final Object ret = fetcher.getService(ctx); //取出服务
    if (sEnableServiceNotFoundWtf && ret == null) {
        // Some services do return null in certain situations, so don't do WTF for them.
        switch (name) {
            case Context.CONTENT_CAPTURE_MANAGER_SERVICE:
            case Context.APP_PREDICTION_SERVICE:
            case Context.INCREMENTAL_SERVICE:
                return null;
        }
        Slog.wtf(TAG, "Manager wrapper not available: " + name);
        return null;
    }
    return ret;
}

查看SystemServiceRegistry.java代码可以看出有个static代码块,在SystemServiceRegistry首次被加载的时候就会执行静态代码块中的代码,把系统服务注册添加到集合SYSTEM_SERVICE_FETCHERS 中,后面使用的时候通过getSystemService从集合中去获取。

static {
    //CHECKSTYLE:OFF IndentationCheck
    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);
        }});

    registerService(Context.ACCOUNT_SERVICE, AccountManager.class,
            new CachedServiceFetcher<AccountManager>() {
        @Override
        public AccountManager createService(ContextImpl ctx) throws ServiceNotFoundException {
            IBinder b = ServiceManager.getServiceOrThrow(Context.ACCOUNT_SERVICE);
            IAccountManager service = IAccountManager.Stub.asInterface(b);
            return new AccountManager(ctx, service);
        }});

    registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
            new CachedServiceFetcher<ActivityManager>() {
        @Override
        public ActivityManager createService(ContextImpl ctx) {
            return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
        }});

    registerService(Context.ACTIVITY_TASK_SERVICE, ActivityTaskManager.class,
            new CachedServiceFetcher<ActivityTaskManager>() {
        @Override
        public ActivityTaskManager createService(ContextImpl ctx) {
            return new ActivityTaskManager(
                    ctx.getOuterContext(), ctx.mMainThread.getHandler());
        }});

    registerService(Context.URI_GRANTS_SERVICE, UriGrantsManager.class,
            new CachedServiceFetcher<UriGrantsManager>() {
        @Override
        public UriGrantsManager createService(ContextImpl ctx) {
            return new UriGrantsManager(
                    ctx.getOuterContext(), ctx.mMainThread.getHandler());
        }});

    registerService(Context.ALARM_SERVICE, AlarmManager.class,
            new CachedServiceFetcher<AlarmManager>() {
        @Override
        public AlarmManager createService(ContextImpl ctx) throws ServiceNotFoundException {
            IBinder b = ServiceManager.getServiceOrThrow(Context.ALARM_SERVICE);
            IAlarmManager service = IAlarmManager.Stub.asInterface(b);
            return new AlarmManager(service, ctx);
        }});

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

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

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

    registerService(Context.HDMI_CONTROL_SERVICE, HdmiControlManager.class,
            new StaticServiceFetcher<HdmiControlManager>() {
        @Override
        public HdmiControlManager createService() throws ServiceNotFoundException {
            IBinder b = ServiceManager.getServiceOrThrow(Context.HDMI_CONTROL_SERVICE);
            return new HdmiControlManager(IHdmiControlService.Stub.asInterface(b));
        }});

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

    registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class,
            new CachedServiceFetcher<ClipboardManager>() {
        @Override
        public ClipboardManager createService(ContextImpl ctx) throws ServiceNotFoundException {
            return new ClipboardManager(ctx.getOuterContext(),
                    ctx.mMainThread.getHandler());
        }});

    // The clipboard service moved to a new package.  If someone asks for the old
    // interface by class then we want to redirect over to the new interface instead
    // (which extends it).
    SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);

    registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
            new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
        @Override
        public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
            IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
            IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
            return new ConnectivityManager(context, service);
        }});

    registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
        @Override
        public IBinder createService() throws ServiceNotFoundException {
            return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE);
        }
    });

    registerService(Context.TETHERING_SERVICE, TetheringManager.class,
            new CachedServiceFetcher<TetheringManager>() {
        @Override
        public TetheringManager createService(ContextImpl ctx) {
            return new TetheringManager(
                    ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE));
        }});


    registerService(Context.IPSEC_SERVICE, IpSecManager.class,
            new CachedServiceFetcher<IpSecManager>() {
        @Override
        public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException {
            IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE);
            IIpSecService service = IIpSecService.Stub.asInterface(b);
            return new IpSecManager(ctx, service);
        }});

    registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
            new CachedServiceFetcher<VpnManager>() {
        @Override
        public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
            IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
            IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
            return new VpnManager(ctx, service);
        }});

    registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
            ConnectivityDiagnosticsManager.class,
            new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
        @Override
        public ConnectivityDiagnosticsManager createService(ContextImpl ctx)
                throws ServiceNotFoundException {
            // ConnectivityDiagnosticsManager is backed by ConnectivityService
            IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
            IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
            return new ConnectivityDiagnosticsManager(ctx, service);
        }});

    registerService(
            Context.TEST_NETWORK_SERVICE,
            TestNetworkManager.class,
            new StaticApplicationContextServiceFetcher<TestNetworkManager>() {
                @Override
                public TestNetworkManager createService(Context context)
                        throws ServiceNotFoundException {
                    IBinder csBinder =
                            ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
                    IConnectivityManager csMgr =
                            IConnectivityManager.Stub.asInterface(csBinder);

                    final IBinder tnBinder;
                    try {
                        tnBinder = csMgr.startOrGetTestNetworkService();
                    } catch (RemoteException e) {
                        throw new ServiceNotFoundException(Context.TEST_NETWORK_SERVICE);
                    }
                    ITestNetworkManager tnMgr = ITestNetworkManager.Stub.asInterface(tnBinder);
                    return new TestNetworkManager(tnMgr);
                }
            })单例模式以及在android中的使用

片段作为 Android 中的单例

单例模式进阶之Android中单例模式的应用场景

Android中的单例模式

(Android开发)关于懒汉模式的并发问题以及规避方法

Android设计模式应用 谈谈Android中的单例模式