应用最广的模式-单列模式(结合Android源码)
2017-10-30 00:00
381 查看
今日科技快讯
据ESPN报道,阿里巴巴集团执行副主席蔡崇信确认以个人出资购买篮网队49%的股份。在蔡崇信个人投资之后,现有股东将继续负责球队的日常运营,篮网队估值共23亿美元。不过据联盟消息人士透露,在接下来的4年里,普罗霍罗夫仍然会是篮网的大老板和运作老板,之后蔡崇信应该拥有完全收购篮网的权利。消息人士还透露,在普罗霍罗夫出售他剩余股份前,53岁的蔡崇信不会监督球队任何的篮球和商业运作,这样的责任现在仍然属于普罗霍罗夫和他的团队。
作者简介
各位小伙伴们大家好,新的一周又开始了,希望大家都能有个好心情迎接新的一周。
本篇来自 浅梦沫汐Fx 的投稿,分享了结合了Android 的源码进行单例模式的分析,希望大家喜欢!
浅梦沫汐Fx[b][b][b][b][b][b][b][b][b][b] [/b] [/b][/b][/b][/b][/b][/b][/b][/b][/b]的博客地址:
http://www.jianshu.com/u/559c1e54ae74
开始
谈起设计模式估计大家都不会陌生,一个项目中至少会用到其中的一种模式,今天要说的主角就是单列,我了大致总结了它的几种用法,同时也结合了Android 的源码进行单例的分析。个人总结了下自我学习的方法,在学习任何一个新的事物的时候,不能盲目的去干,而应适当的采取一定的技巧,我大致分了三大步:
要知道这个东西是什么,这个东西有啥用,一般用在啥地方;
这个东西该怎么用,我平时有没有遇到类似的用法;
熟悉了用法之后,总结下为什么别人那样去写,这样写的优缺点是什么,我能不能仿写下或者能不能改写下别人的代码,进行深度的总结下,然后用于到实践中,记住,看完了,千万不要就丢掉了,东西太多了,也许今天记住了,明天就会忘记,所以最好写几个案列实践下。
单例模式定义及应用场景
定义
确保这个类在内存中只会存在一个对象,而且自行实例化并向整个系统提供这个实例。
应用场景
一般创建一个对象需要消耗过多的资源,如:访问I0和数据库等资源或者有很多个地方都用到了这个实例。
单例模式的几种基本写法
饿汉式:
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ 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 方法都会进行同步,消耗不必要的资源。饿汉式缺点:不需要的时候就加载了,造成资源浪费。
双重检查单例( DCL 实现单例):
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
这种写法估计是我们在开发中最常用的,这次代码的亮点是是在 getInstance() 方法中进行了双重的判断,第一层判断的主要避免了不必要的同步,第二层判断是为了在 null 的情况下再去创建实例;举个简单的列子:假如现在有多个线程同时触发这个方法: 线程 A 执行到 instance = new Singleton()
,它大致的做了三件事:
给 Singleton 实例分配内存,将函数压栈,并且申明变量类型;
初始化构造函数以及里面的字段,在堆内存开辟空间;
将 instance 对象指向分配的内存空间;
这种写法也并不是保证完全100%的可靠,由于 java 编译器允许执行无序,并且 jdk1.5之前的jvm ( java 内存模型)中的 Cache,寄存器到主内存的回写顺序规定,第二个和第三个执行是无法保证按顺序执行的,也就是说有可能1-2-3也有可能是1-3-2; 这时假如有 A 和 B 两条线程, A线程执行到3的步骤,但是未执行2,这时候 B 线程来了抢了权限,直接取走 instance 这时候就有可能报错,同时我也放了一张内存模型,帮助大家更好的去理解,如图:
简单总结就是说jdk1.5之前会造成两个问题:
线程间共享变量不可见性;
无序性(执行顺序无法保证);
当然这个bug已经修复了,SUN官方调整了JVM,具体了Volatile关键字,因此在jdk1.5之前只需要写成这样既可, private Volatitle static Singleton instance; 这样就可以保证每次都是从主内存中取,当然这样写或多或少的回影响性能,但是为了安全起见,这点性能牺牲还是值得。
双重检查单列(DCL)
优点:资源利用率高,第一次执行方法是单例对象才会被实例化;
缺点:第一次加载时会稍慢,jdk1.5之之前有可能会加载会失败;
举个例子,Android 常用的框架: Eventbus( DCL 双重检查)
static volatile EventBus defaultInstance; public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
静态内部内实现单例:
public class Singleton { private static Singleton instance; private Singleton() { } public static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
这种方式不仅确保了线程的安全性,也能够保证对象的唯一性,同时也是延迟加载,很多技术大牛也是这样推荐书写。
枚举实现单例:
public enum SingletonEnum { INSTANCE; public void doSomething() { } }
优点:相对于其他单例来说枚举写法最简单,并且任何情况下都是单列的,JDK1.5之后才有的。
使用容器单例:
public class SingletonManager { private static Map<String, Object> objMap = new HashMap<>(); private SingletonManager() { } public static void putObject(String key, String instance){ if(!objMap.containsKey(key)){ objMap.put(key, instance); } } public static Object getObject(String key){ return objMap.get(key); } }
在程序开始的时候将单例类型注入到一个容器之中,也就是单例 ManagerClass ,在使用的时候再根据 key 值获取对应的实例,这种方式可以使我们很方便的管理很多单例对象,也对用户隐藏了具体实现类,降低了耦合度;但是为了避免造成内存泄漏,所以我们一般在生命周期销毁的时候也要去销毁它。
Android源码分析
说了这么多,也写了这么多,一起来看看Android源码中是如何实现单例的,今天的的重点就是LayoutInflater这个类,看下 LayoutInflater 的单例模式实现:
LayoutInflater mInflater = LayoutInflater.from(this);
上边的写法估计没有人会陌生,获取 LayoutInflater 的实例,我们一起看看具体的源码实现:
/** * Obtains the LayoutInflater from the given context. */ public static LayoutInflater from(Context context) { //调用Context getSystemService()方法获取; LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; }
Context 类:
public abstract Object getSystemService(@ServiceName @NonNull String name);
我们知道 Context 是一个抽象类,那么到底是那个类具体实现的了,我们Ctrl + H(window) 一下,看看他到底有哪些子类,看下图:
这么多类怎么找,一个类一类的去翻吗?既然不能从这个地方下手,那就只能改走其他的道路,那我就从入口函数开始,也就是我们熟悉的main函数 在 Android 中ActivityThread 类中,看主要的方法和类:
### ActivityThread thread = new ActivityThread(); 主要看thread的attach(false)方法: public static void main(String[] args) { 省略....... //初始化thread ActivityThread thread = new ActivityThread(); thread.attach(false); 省略....... }
private void attach(boolean system) { //是不是系统应用:传递的false if (!system) { 省略...... final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { // Ignore } 省略...... }
在 main 方法中初始化 ActivityThread 的实例,并且调用了 attach 方法传入 false ,证明不是系统应用,紧接着获取了 IActivityManager 实例,其实也就是 ActivityManagerService 的对象,他们的关系图如下:
接着调用 attachApplication(mAppThread); 绑定当前的 ApplicationThread 接着往下走,看看 attachApplication(mAppThread) 方法,还是一样的抓住核心,只看重点:
@Override public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); } }
这个方法逻辑就很简单了锁定当前的thread和pid 接着继续往下走到 ActivityManagerService:
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { 省略....... thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked()); //省略........ // See if the top visible activity is waiting to run in this process... if (normalMode) { try { if (mStackSupervisor.attachApplicationLocked(app)) { didSomething = true; } } catch (Exception e) { Slog.wtf(TAG, "Exception thrown launching activities in " + app, e); badApp = true; } } //下边主要是介绍了些receiver和broadcast 这些都不是重点主要看和app有关的, 所以就省略掉了;
这个方法代码很长但是逻辑并不是很复杂,有两个重要的方法: thread.bindApplication() 和 attachApplicationLocked(app) bindApplication; 见名知意,将thread 绑定到 ActivityManagerService 中,那我们来看看 attachApplicationLocked(app)
这个方法:
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException { final String processName = app.processName; boolean didSomething = false; for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); if (!isFrontStack(stack)) { continue; } //返回当前栈顶的activity ActivityRecord hr = stack.topRunningActivityLocked(null); if (hr != null) { if (hr.app == null && app.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { //真正的开启activity;原来找了半天就是这个家伙; if (realStartActivityLocked(hr, app, true, true)) { didSomething = true; } } catch (RemoteException e) { Slog.w(TAG, "Exception in new application when starting activity " + hr.intent.getComponent().flattenToShortString(), e); throw e; } } } } } }
一看这个名字就知道肯定和 Activity 的任务栈有关的,当前内部持有一个 ActivityStack,相当于 ActivityStack 的辅助管理类,realStartActivityLocked(hr, app, true, true) 而这个方法是真正的去开启 activity
的:
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { //省略..主要是检查一些配置信息和设置相关的参数等等........ //参数设置完毕终于准备启动activity了,发车了; app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); //省略........... }
重点的东西来了既然这个方法是用来开启 activity 的我猜想他肯定和 Context 有关,既然和Context 有关那么也就能找到 Context 的子类带这个目标我们出发进入到 ActivityThread:
@Override public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; r.referrer = referrer; r.voiceInteractor = voiceInteractor; r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; r.profilerInfo = profilerInfo; r.overrideConfig = overrideConfig; updatePendingConfiguration(curConfig); sendMessage(H.LAUNCH_ACTIVITY, r); }
这个方法准备了要启动的 activity 的一些信息,重要的一点他利用 Handler 发送了一个消息, sendMessage(H.LAUNCH_ACTIVITY, r); 我们来找找这个接收消息的地方:
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); //最终调用 handleLaunchActivity(r, null); } 省略.............. }
我们接着具体看看 handleLaunchActivity(r, null); 这个方法到底做了什么东西:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { //省略....... Activity a = performLaunchActivity(r, customIntent); . //返回Activity对象;我们经常用到Context的时候就传入this,我猜想Activity的创建和Context应该是少不了的关联,没办法只能接着找; } 省略........
performLaunchActivity 代码太多我本来想只是截取一部分,可是看了好久感觉还是贴出来一大部分吧,毕竟都是比较重要的:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 省略...... //终于找到了activity的创建了;你用类加载器采用反射机制; Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } ........... //初始化Application try { Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) { //获取当前activity的Context 终于还是给我找到了... Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); ......... 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); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; r.stopped = true; if (!r.activity.mFinished) { activity.performStart(); r.stopped = false; } if (!r.activity.mFinished) { if (r.isPersistable()) { if (r.state != null || r.persistentState != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); } } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); }
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { //省略........ ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, displayId, r.overrideConfig); appContext.setOuterContext(activity); Context baseContext = appContext; //省略....还好代码不多;感谢老铁 这个不就是我要找到的他的实现类吗.....赶紧看看,对了之前的方法可别忘了
进入 ContextImpl 类:
@Override public Object getSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); }
一步一步点下去:
/** * Gets a system service from a given context. 注释写的多清楚,哈哈 */ public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); return fetcher != null ? fetcher.getService(ctx) : null; }
看看真面目:
HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =new HashMap<String, ServiceFetcher<?>>();
原来就是 HashMap 存贮,也就是我上边写的最后一种单例方式容器存贮,其实写到这里还并没有写完了,既然我们是直接获取的也并没有自己进行注入,那么你想过没有那么到底系统是啥时候给我们注入的了,带这个问题,我们在翻翻源码进入 SystemServiceRegistry 类,我们看看这个
HashMap 到底是啥时候注入的只关心这个 map 集合就好了,搜索一下:
/** * 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_FETCHERS.put(serviceName, serviceFetcher); }
原来在这地方调用,那到底是啥时候调用的这个 registerService 还是需要搜索一下,这就比之前简单多了:
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.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new PhoneLayoutInflater(ctx.getOuterContext()); }});
总结
原来我们 app 已启动的时候下边已经多了大量的工作,在第一次加载类的时候就会注册各种 ServiceFetcher ,将这些以键值对的形式存进去,需要用到的时候在通过 key 去取值,到此现在这个流程基本上明白了,那我就用一个流程图来再一次的回头整理下图,只是贴出了一些重要的方法以便回顾之前看的:
其实分析了这么多的源码,说到底就是一个核心原理就是构造私有,并且通过静态方法获取一个实例,在这个过程中必须保证线程的安全性;如果觉得写的不错的给个赞哦,写的有问题的地方希望大家能给你纠正,谢谢!
欢迎长按下图 -> 识别图中二维码
或者 扫一扫 关注我的公众号
相关文章推荐
- 单列模式总结(结合Android源码分析)
- Android源码学习之观察者模式应用
- Android源码学习之单例模式应用
- Android源码学习之单例模式应用及优点介绍
- Android源码学习之工厂方法模式应用
- 结合Android源码分析总结单例模式的几种实现方式
- 23种设计模式和在JDK/Android源码中的应用
- 谈谈23种设计模式在Android源码及项目中的应用
- Android源码学习之组合模式定义及应用
- Android源码学习之装饰模式应用
- Android源码学习之装饰模式应用
- Android源码学习之模板方法模式应用
- Android源码学习之设计模式应用小整理
- Android源码学习之装饰模式应用
- Android源码学习之组合模式应用
- Android装饰者模式介绍(结合源码分析)
- Android源码学习之模板方法模式应用
- Android源码学习之装饰模式应用
- Android源码学习之装饰模式应用
- Android源码学习之观察者模式应用及优点介绍