@Beeder
2017-07-06T10:11:16.000000Z
字数 14447
阅读 863
android
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式应该是日常使用最为广泛的一种模式了。他的作用是确保某个类只有一个实例,避免产生多个对象消耗过多的资源。比如对数据库的操作时,就可以使用单例模式。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
这个实现的核心在于将Singleton类的构造方法私有化,使得外部程序不能通过构造函数来构造Singleton对象。这种写法是在类装载时就实例化instance,他避免了多线程的同步问题。但是不能保证有别的方式去装载,没有达到懒加载。
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
达到了懒加载,但是在多线程不能正常工作。
不建议使用
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种写法能够在多线程中很好的工作,但是每次调用getInstance方法都会进行同步,反应稍慢,会消耗不必要的资源,还会造成不必要的开销,所以这种不建议使用。
public class Singleton {
private volatile static Singleton mSingleton;
private Singleton (){
}
public static Singleton getSingleton() {
if (mSingleton == null) {
synchronized (Singleton.class) {
if (mSingleton == null) {
mSingleton = new Singleton();
}
}
}
return mSingleton;
}
}
这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是为了在null的情况下创建实例。
DCL的优点:资源利用率高,第一次执行mSingleton时单例对象才会被实例化,效率高。
DCL的缺点:第一次加载时反应稍慢,也由于Java内存模型的原因偶尔会失败。在高并发环境下也有一定的缺陷,虽然发生概率很小。
DCL模式是使用最多的单例实现方式,它能够在需要的时候才实例化单例对象,并且能够在绝大多数的场景下保证单例对象的唯一性,除非你的代码在高并发场景比较复杂或者低于JDK 1.6版本下使用,否则,这种方式一般能够满足要求。
JDK1.5之前会有DCL失效问题
原因分析:
假设线程A执行到 mSingleton = new Singleton() 语句,看起来像是一句代码,但实际上它并不是一个原子操作,这句代码最终会被编译成多条汇编指令,它大致做了3件事情:
但是,由于java编译器允许处理器乱序执行,以及JDK1.5之前JMM(Java Memory Model,即java内存模型)中的Cache、寄存器到主内存回写顺序的规定,上面的第二和第三顺序是无法保证的。也就是说,执行顺序可能是1-2-3也可能是1-3-2。如果是后者,并且在3执行完毕、2未执行之前,被切换到线程B上,这时候mSingleton因为已经在线程A内执行过了第三点,mSingleton已经是非空了,所以,线程B直接取走mSingleton,再使用时就会报错,这就是DCL失效的问题,而且这种难以跟踪难以重视的错误很可能会隐藏很久。
在JDK1.5之后,SUN官方已经注意到这个问题,调整了JVM,具现化了volatile关键字,因此,如果JDK是1.5或之后的版本,mSingleton的定义改成private volatile static Singleton mSingleton = null
就可以保证mSingleton对象每次都是从主内存中读取,就可以使用DCL的写法完成单例模式。当然,volatitle 或多或少也会影响到性能,但考虑到程序的正确性,牺牲这点性能还是值得的。
推荐使用
由于DCL单例会在某些情况下出现 “双重检查锁定(DCL)失效” 的问题,在《Java并发编程实践》一书最后指出这种“优化”是丑陋的,不赞成使用。而建议用如下的代码代替:
public class Singleton {
private Singleton (){
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
*静态内部类
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
这种是推荐使用的单例模式实现方式。当第一次加载Singleton类时并不会初始化INSTANCE,只有在第一次调用getInstance方法时才会导致INSTANCE被初始化。这种方式不仅能够保证线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化。
推荐使用
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();
private SingletonManager() {
}
public static void registerService(String key, Objectinstance) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;
}
}
public static ObjectgetService(String key) {
return objMap.get(key) ;
}
}
将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
不管以哪种形式实现单例模式,它们的核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全,防止反实例化导致重新生成实例对象等问题。选择哪种实现方式取决于项目本身,如是否是复杂的并发环境,JDK版本是否过低、单例对象的资源消耗等。
类型 | 简单描述 | 存在问题 |
---|---|---|
饿汉模式 | [基本]静态方法get静态对象,类装载时就实例化对象 | 没有达到懒加载 |
懒汉模式 | 使用synchronized关键字,但每次get时都会进行同步 | 达到懒加载,但反应比较慢 |
DCL单例 | 使用volatile关键字,get时先判空再进行同步 | JDK1.5 之前无volatile关键字 |
静态内部类单例模式 | 使用静态内部类,外部使用静态方法get调用 | 推荐使用 |
枚举单例 | 推荐使用 | |
容器实现单例 | 实例一个静态Map,通过put和get管理 |
优点:
缺点:
在Android系统中,我们经常会通过Context获取系统级别的服务,如WindowsManagerService、ActivityManagerService等,更常用的是一个LayoutInflater的类,这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过Context的getSystemService(String name)获取。
源码路径:frameworks/base/core/java/android/view/LayoutInflater.java
通常我们使用LayoutInflater.from
来获取LayoutInflater服务,LayoutInflater.from(Context)
的实现如下:
/**
* 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;
}
可以看出from(Context)
函数内部调用的是Context类的getSystemService(String key)
方法,我们跟踪到Context类看到,该类是抽象类:
//相关源码路径:frameworks/base/core/java/android/content/Context.java
public abstract class Context {
//省略
}
getView 中使用的Context对象的具体实现类是什么呢?其实在Application、Activity、Service中都会存在一个Context对象,即Contest的总个数Activity个数+Service个数+1,而ListView通常都是显示在Activity中,那么我们就以Acitviy中的Context来分析。
我们知道,一个Activity的入口是ActivityThread的main函数,在main函数中创建一个新的ActivityThread的对象,并且在启动消息循环(UI线程)、创建新的Activity、新的Context对象,然后将该Context对象传递给Activity。下面看到ActivityThread源代码:
//相关源码路径:frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread {
//省略
public static void main(String[] args) {
//省略
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
//创建ActivityThread对象
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//省略
Looper.loop();
}
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
//不是系统应用的情况
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
//关联mAppThread
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
//省略
} else {
//省略
}
}
在main方法中,我们创建了一个Activitythread对象后,调用了其attach函数,并且参数为false。在attach函数中,在参数为false的情况下(非系统应用),会通过Binder机制与ActivityManagerService通信,并且最终调用handleLaunchActivity函数,我们看看该函数的实现:
//相关源码路径:frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread {
//省略
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//省略
Activity a = performLaunchActivity(r, customIntent);
//省略
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
//1、创建Acitvity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//省略
}
} catch (Exception e) {
//省略
}
}
try {
//创建Application对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//省略
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
//2、获取Context对象
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
//3、将appContext等对象attach到activity中
activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
//省略
//4、调用Activity中的Create方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
//省略
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
//省略
}
return activity;
}
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
//5、创建Context对象,可以看到实现类是ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
return baseContext;
}
}
通过上面的1-5注释处的代码分析可知道,Context的实现类为ContextImpl,其最终返回一个ContextImpl实例。
//路径:frameworks/base/core/java/android/app/ContextImpl.java
class ContextImpl extends Context {
private final static String TAG = "ContextImpl";
// The system service cache for the system services that are cached per-ContextImpl.
/用SystemServiceRegistry类.createServiceCache()创建系统服务
f实现。inal Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
/*可以看到返回的是一个ContextImpl*/
static ContextImpl createActivityContext(ActivityThread mainThread,LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
return new ContextImpl(null, mainThread, packageInfo, null, null, false,null, overrideConfiguration, displayId);
}
}
由ContextImpl类代码可以看到,创建系统服务时,调用的是SystemServiceRegistry类的createServiceCache方法。
//路径:frameworks/base/core/java/android/app/SystemServiceRegistry.java
final class SystemServiceRegistry {
//实际返回Object[sServiceCacheSize]
public static Object[] createServiceCache() {
return new Object[sServiceCacheSize];
}
/**
* Base interface for classes that fetch services.
* These objects must only be created during static initialization.
*/
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.
*/
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);
}
}
在SystemServiceRegistry类中, 主要通过抽象内部类SystemServiceRegistry类控制service对象,如果没有service对象则通过实现抽象方法createServic来创建。
//路径:frameworks/base/core/java/android/app/SystemServiceRegistry.java
final class SystemServiceRegistry {
//1、Service 容器
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
//3、静态代码块,第一次加载该类时执行(只执行一次,保证实例的唯一性)
static{
//省略
//注册LayoutInflater service
//调用上述代码的CachedServiceFetcher内部类创建系统服务对象
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
}
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
*/
//2、注册服务器
private static <T> void registerService(String serviceName, Class<T> serviceClass,ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
//将Service对象加入Service容器
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
}
SystemServiceRegistry类中,1-3注释处代码为注册及创建Service对象的具体实现过程。
在静态代码块中。通过调用registerService方法,实现抽象内部类CachedServiceFetcher,并实现CachedServiceFetcher类的抽象方法createService方法创建Service对象,并将对象加入Service容器中。
结合上述代码可以看到,在虚拟机第一次加载时会注册各种serviceFetcher,其中就包含了LayoutInflater Service。将这些服务以键值对的形式储存在一个HashMap钟,用户使用时只需要根据key来获取到对应的serviceFetcher,然后通过serviceFetcher对象的getService函数来获取具体的服务对象。当第一次获取时,会调用serviceFetcher的CreatService函数创建服务对象,然后将该对象缓存到一个列表中,下次再获取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。这种模式就是‘通过容器实现的单例模式’,系统核心服务以单例形式存在,减少了资源。
LayoutInflater在我们的开发中扮演着重要的角色,但很多时候我们都不知道它的重要性,因为它的重要性被隐藏在了Activity、Fragment等组件的光环之下。
LayoutInflater是一个抽象类,具体代码如下:
//代码:frameworks/base/core/java/android/view/LayoutInflater.java
public abstract class LayoutInflater {
//代码省略
}
既然是抽象不是具体的,那我们就必须找出它的实现类。这需要先从LayoutInflater的起源开始,在上文中知道,在加载ContextImpl类时,会调用SystemServiceRegistry类,在SystemServiceRegistry类的静态代码块中会通过如下代码将LayoutInflater的serviceFetcher注入到Service容器中。具体代码如下:
//路径:frameworks/base/core/java/android/app/SystemServiceRegistry.java
final class SystemServiceRegistry {
static {
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
//创建Service实例
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
}
}
结果这里调用了PhoneLayoutInflater类,具体如下:
//路径:frameworks/base/core/java/com/android/internal/policy/PhoneLayoutInflater.java
public class PhoneLayoutInflater extends LayoutInflater {
//内置View类型的前缀,如TextView的完整路径是android.widget.TextView
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
//代码省略
/** Override onCreateView to instantiate names that correspond to the
widgets known to the Widget factory. If we don't find a match,
call through to our super class.
*/
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
//在View名字的前面添加前缀来构造View的完整路径。
//例如类名为TextView,那么TextView的完整路径为android.widget.TextView
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
} catch (ClassNotFoundException e) {
// In this case we want to let the base class take a crack
// at it.
}
}
return super.onCreateView(name, attrs);
}
}
PhoneLayoutInflater类的核心是覆写了LayoutInflater的onCreateView方法,该方法在传递进来的View名字前加上前缀以得到该内置View的完整路径。最后根据完整路径来构造对应的View对象。
以Activity的setContentView为例,先来看看其实现:
//路径:frameworks/base/core/java/android/app/Activity.java
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
}
Activity的setContentView方法实际上调用的是Window的setContentView,而Window是一个抽象类,其具体实现类为PhoneWindow,PhoneWindow类中对应的方法如下:
//路径:frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
//1、当mContentParent为空时先创建DecorView
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
}