109  
查询码:00000103
Android设计模式之单例模式
来源:https://blog.csdn.net/zhangying1994/article/details/83239916
作者: 朱凡 于 2021年02月13日 发布在分类 / FM组 / FM_App 下,并于 2021年02月13日 编辑
模式 一个 android public 对象 static private 设计 service 实例

Android设计模式之单例模式


单例模式的定义:

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。


实现单例模式的关键点:

1.构造函数不对外开放,一般为private。

2.通过一个静态方法或者枚举返回单例类对象。

3.确保单例类的对象有且只有一个,尤其是在多线程环境下。

4.确保单例类对象在反序列化时不会重新构建对象。


1.饿汉式 单例模式 (在声明的时候已经初始化了)

public class Singleton {
  private static final Singleton s = new Singleton();
  private Singleton () {

  }

  public static Singleton getIntance() {
    return s;
  }
}


2.懒汉式 单例模式

public class SingletonTwo {
  private static SingletonTwo singletonTwo;

  private SingletonTwo () {

  }

  public static synchronized SingletonTwo getInstance() {

  if (singletonTwo == null) {
    singletonTwo = new SingletonTwo();
  }
  return singletonTwo;
  }
}


上面通过添加 synchronized关键字,使得getInstance()是一个同步方法,保证多线程情况下单例对象的唯一性。


懒汉式优点:

1.单例只有在使用时才会被实例化,在一定程度上节约了资源。

缺点:

1.第一次加载时需要及时进行实例化,反应稍慢。

2.每次调用getIntance都进行同步,造成不必要的同步开销。




3.Double Check Lock(DCL) 实现单例

public class DoubleSingleInstance {

  private static DoubleSingleInstance sSingleInstance;

  private DoubleSingleInstance() {

  }

  public static DoubleSingleInstance getIntance() {

    if (sSingleInstance == null) {
      synchronized (DoubleSingleInstance.class) {

        if (sSingleInstance == null) {

          sSingleInstance = new DoubleSingleInstance();

          }

        }
      }

    return sSingleInstance;
  }
}


在getInstance()中对instance进行两次判空,第一层判空:避免不必要的同步。第二次判空:在instance为null的情况下创建实例。



/*****/

假设线程A执行到sInstance = new Singletion()语句,这里看起来是一句代码,但实际上不是一个原子操作。

最终

被编译成多条汇编指令:

1.给Singletom的实例分配内存;

2.调用Singleton()的构造函数,初始化成员字段;

3.将sInstance对象指向分配的内存空间(此时sInstance就不是null了)。


由于java编译器允许处理器乱序执行。(可能导致单例对象的某些字段没有初始化)


3.1在JDK1.5之后,SUN发现该问题,调整了JVM,具体化了volatile关键字。

所以在JDK1.5以及其之后,只需要将sInstance定义成private volatile static Singleton sInstance = null,就可以保证sInstance对象每次都是从主内存中读取。




4.静态内部类单例模式


DCL虽然在一定程度上解决了资源消耗、多余同步、线程安全等问题。

(但还是会存在 双重检查锁定(DCL)失效)

public class InnerSingleton {

  private InnerSingleton() {

  }

  public static InnerSingleton getInstance() {
    return SingletonHolder.sIntance;
  }
  /**
   * 静态内部类
   */

  private static class SingletonHolder{

  private static final InnerSingleton sIntance = new InnerSingleton();

  }
}



使用静态内部类单例模式的优点:

第一次加载Singleton类时并不会初始化sIntance,只有第一次调用Singleton的getInstance方法才会导致sInstance被初始化。

第一次调用getInstance()方法会导致虚拟机加载SingletonHolder类。

优点:

保证线程安全。

保证单例对象的唯一性。

延迟单例的实例化。


5.枚举单例

public enum SingleEnum {
  INSTANCE;
}


最重要的是默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。



在1~4种创建单例模式的方法中,在反序列化的情况下会重新创建对象。


我们知道通过序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效获取一个实例。


即使构造函数是私有的,反序列化依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。


反序列化操作提供了一个很特别的钩子函数,类中具有一个私有的readResolve()函数,这个函数可以让开发人员控制对象的反序列化。

public class SingletonSerializable implements Serializable{

  private static final long serialVersionUID = 0L;
  private static final SingletonSerializable INSTANCE = new SingletonSerializable();

  private SingletonSerializable() {

  }

  public static SingletonSerializable getInstance() {
    return INSTANCE;
  }

  private Object readResolve() throws ObjectStreamException{
    return INSTANCE;
  }
}



也就是在readResolve方法中将单例对象返回,而不是重新生成一个新对象。

对于枚举,并不存在这个问题,因为即使反序列化它也不会重新生成新的实例。


(1)可序列化类中的字段类型不是java的内置类型,那么该字段类型也需要实现Serializable接口。


(2)如果你调整了可序列化类的内部结构,例如新增、去除某个字段,但是没有修改serialVersionUID,那么会引发java.io.InvalidClassException 或者导致某个属性为0或者null,此时最好的方案是我们直接将serialVersionUID设置为0L,这样即使修改了内部结构,我们反序列化也不会抛出java.io.InvalidClassException ,只是那些新增的字段会为0或者null。



6.使用容器实现单例模式

public class SingletonManager {

  private static Map<String, Object> objMap = new HashMap<String, Object>();

  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获取对应类型的对象。


单例模式核心:


1.将构造函数私有化。

2.通过静态方法获取唯一的实例。

3.保证线程安全

4.防止反序列化导致重新生成实例对象等问题


//Android framework中大多使用静态内部类的方式实现单例。
private static final Singleton<IActivityManager> IActivityManagerSingleton =
      new Singleton<IActivityManager>() {
        @Override
        protected IActivityManager create() {
          final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
          final IActivityManager am = IActivityManager.Stub.asInterface(b);
          return am;
        }
      };





package android.util;

/**
 * Singleton helper class for lazily initialization.
 *
用于延迟初始化的Singleton helper类。
 * Modeled after frameworks/base/include/utils/Singleton.h
 *
 * @hide
 */
public abstract class Singleton<T> {
  private T mInstance;

  protected abstract T create();

  public final T get() {
    synchronized (this) {
      if (mInstance == null) {
        mInstance = create();
      }
      return mInstance;
    }
  }
}

Android源码中的单例模式:

在android系统中,通过Context获取系统级别的服务,如WindowManagerService,ActivityManagerService等,常用是LayoutInflater,这些服务会在合适的时候以单例的形式注册在系统中, 这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候通过Context的getSystemService(String name)获取。

#LayoutInflater
/**
 * Obtains the LayoutInflater from the given context.
 */
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;
}



#ContextImpl
public class ContextImpl extends Context {
  ... ...
  ... ...
}

#ContextImpl
@Override
public Object getSystemService(String name) {
  return SystemServiceRegistry.getSystemService(this, name);
}

#ContextImpl    
@Override
public Object getSystemService(String name) {
  return SystemServiceRegistry.getSystemService(this, name);
}

每一个ContextImpl对象都会初始化一个mServiceCache数组,这个数组的大小就是系统服务的数量。

#ContextImpl
// The system service cache for the system services that are cached per-ContextImpl.
 final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();


通过Context对象获取系统服务对象。

/**
 *管理所有系统服务 ,SystemServiceRegistry 起到一个工具类的作用,其构造方法私有。
 * Manages all of the system services that can be returned by {@link Context#getSystemService}.
 * Used by {@link ContextImpl}.
 * @hide
 */
public final class SystemServiceRegistry {
    ... ...
}

SystemServiceRegistry首次加载。

以LayoutInflater注册为例:registerService方法注册LayoutInflater时,需要参数Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,以及CachedServiceFetcher对象。

registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});


/**

* Gets the name of the system-level service that is represented by the specified class.

*获取由指定类表示的系统级服务的名称。

*/

public static String getSystemServiceName(Class<?> serviceClass) {

return SYSTEM_SERVICE_NAMES.get(serviceClass);

}


// Service registry information.

//静态初始化完成后,此信息永远不会更改

// This information is never changed once static initialization has completed.

private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =

new HashMap<Class<?>, String>();

//Service容器

private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =

new HashMap<String, ServiceFetcher<?>>();


private static int sServiceCacheSize;

/**

* 从给定的上下文获取系统服务。

* Gets a system service from a given context.

*/

//根据key获取对应的服务

public static Object getSystemService(ContextImpl ctx, String name) {

//根据name获取对应的服务

ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);

return fetcher != null ? fetcher.getService(ctx) : null;

}



/**

*提取服务的类的基本接口。这些对象只能在静态初始化期间创建。

* Base interface for classes that fetch services.

* These objects must only be created during static initialization.

* (竟然还可以是abstract 的接口)

*/

public static abstract interface ServiceFetcher<T> {

T getService(ContextImpl ctx);

}

/**

* Override this class when the system service constructor needs a

* ContextImpl and should be cached and retained by that context.

* 当系统服务构造函数需要ContextImpl时覆盖此类,并且应该由该上下文进行缓存和保留。

*/

public 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;//获取Service缓存

synchronized (cache) {

// Fetch or create the service.

Object service = cache[mCacheIndex];

if (service == null) {

try {

service = createService(ctx);

cache[mCacheIndex] = service;

} catch (ServiceNotFoundException e) {

onServiceNotFound(e);

}

}

return (T)service;

}

}

//子类覆写该方法以创建服务对象

public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;

}


/**

* 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 ctx) {

synchronized (StaticServiceFetcher.this) {

if (mCachedInstance == null) {

try {

mCachedInstance = createService();

} catch (ServiceNotFoundException e) {

onServiceNotFound(e);

}

}

return mCachedInstance;

}

}


public abstract T createService() throws ServiceNotFoundException;

}


/**

* 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 .

try {

mCachedInstance = createService(appContext != null ? appContext : ctx);

} catch (ServiceNotFoundException e) {

onServiceNotFound(e);

}

}

return mCachedInstance;

}

}


public abstract T createService(Context applicationContext) throws ServiceNotFoundException;

}

/**

* Statically registers a system service with the context.

* This method must be called during static initialization only.

* 用上下文静态注册系统服务。

* 该方法只能在静态初始化期间调用。

*/

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);

}



//静态代码块,第一次加载该类的时候执行(只执行一次,保证实例的唯一性)

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);

}});



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());

}});

... ...

}

从ContextImpl类的部分代码可以看到,在虚拟机第一次加载该类的时候会注册各种ServiceFatcher,其中就包含了LayoutInflater Service。


将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需要根据key来获取对应的ServiceFetcher,然后通过ServiceFetcher对象的getService方法来获取具体的服务对象。


当第一次获取是,会调用ServiceFetcher的createService方法创建服务对象,然后将该对象缓存到一个列表中,下次再取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。


通过HashMap容器的单例模式实现方式,系统核心服务以单例形式存在,减少了资源消耗。





 推荐知识

 历史版本

修改日期 修改人 备注
2021-02-13 12:59:38[当前版本] 朱凡 创建版本

 附件

附件类型

GIFGIF

知识分享平台 -V 4.8.7 -wcp