应用最广的模式-单例模式(结合Android源码)

谈起设计模式估计大家都不会陌生,一个项目中至少会用到其中的一种模式,今天要说的主角就是单列,我了大致总结了它的几种用法同时也结合了Android的源码进行单列的分析;

好了正题开始了,其实个人总结了下自我学习的方法,在学习任何一个新的事物的时候,不能盲目的去干,而应适当的采取一定的技巧性东西,OK;

我大致分了三大步:

1:要知道这个东西是个什么玩意,这个东西有啥用,一般用在啥地方;
2:这个东西该怎么用了,我平时有没有遇到类似的用法;
3:熟悉了用法之后,总结下为什么别人那样去写,这样写的优缺点是什么,我能不能仿写下或者能不能改写下别人的代码,进行深度的总结下,然后用于到实践中,记住,看完了,千万不要就丢掉了,东西太多了,也许今天记住了,明天就会忘记,所以最好写几个案列实践下;实践是检验真理的位移标准,不是吗;

1: 单列模式的定义以及应用场景

1.1 定义:

确保这个类在内存中只会存在一个对象,而且自行实例化并向整个系统提供这个实例;

1.2 场景:

一般创建一个对象需要消耗过多的资源,如:访问I0和数据库等资源或者有很多个地方都用到了这个实例;

2:单列模式的几种基本写法:

2.1 最常见的写法:(懒汉式和饿汉式)
2.1.1 饿汉式
public class Singleton {

    private static final Singleton INSTANCE=new Singleton();

    private Singleton(){
    }

    public static Singleton getInstance(){
        return INSTANCE;
    }
}
2.1.1 懒汉式
public class Singleton {

    private static Singleton instance;

    private Singleton(){

    }
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

上边的两种是最常见的,顾名思义懒汉式和饿汉式,一个是拿时间换空间,一个是拿空间换时间,懒汉式只有我需要他的时候才去加载它,懒加载机制,饿汉式不管需不需要我先加载了再说,先在内存中开辟一块空间,占用一块地方,等用到了直接就拿来用.这两种是最基本的单列模式,

2.1.2.1懒汉式:

懒汉式缺点:效率低,第一次加载需要实例化,反应稍慢。每次调用getInstance方法都会进行同步,消耗不必要的资源。

2.1.1.1饿汉式:

缺点:不需要的时候就加载了,造成资源浪费.

2.2 双重检查单列(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执行到nstance = new Singleton(),它大致的做了三件事:
(1):给Singleton实例分配内存,将函数压栈,并且申明变量类型;
(2):初始化构造函数以及里面的字段,在堆内存开辟空间;
(3):将instance对象指向分配的内存空间;

应用最广的模式-单例模式(结合Android源码)_第1张图片
演示图.png

这种写法也并不是保证完全100%的可靠,由于java编译器允许执行无序,并且jdk1.5之前的jvm(java内存模型)中的Cache,寄存器到主内存的回写顺序规定,第二个和第三个执行是无法保证按顺序执行的,也就是说有可能1-2-3也有可能是1-3-2; 这时假如有A和B两条线程,A线程执行到3的步骤,但是未执行2,这时候B线程来了抢了权限,直接取走instance这时候就有可能报错,同时我也放了一张内存模型,帮助大家更好的去理解,如图;

应用最广的模式-单例模式(结合Android源码)_第2张图片
内存模型.png
简单总结就是说jdk1.5之前会造成两个问题

1:线程间共享变量不可见性;
2:无序性(执行顺序无法保证);
当然这个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;
    }
2.3 静态内部内实现单列
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;
    }
}

这种方式不仅确保了线程的安全性,也能够保证对象的唯一性,同时也是延迟加载,很多技术大牛也是这样推荐书写;

2.4 枚举实现单列
public enum SingletonEnum {

    INSTANCE;
    public void doSomething() {

    }
}

优点:相对于其他单列来说枚举写法最简单,并且任何情况下都是单列的,JDK1.5之后才有的;

2.5 使用容器单列
public class SingletonManager {

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

    private SingletonManager() {

    }

    public static void putObject(String key, Object 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是一个抽象类,那么到底是那个类具体实现的了,我们C+H(window)一下,看看他到底有哪些子类,看下图;


应用最广的模式-单例模式(结合Android源码)_第3张图片
Paste_Image.png

我擦,是不是搞事情,这么多类怎么找,一个类一类的去翻吗?既然不能从这个地方下手,那就只能改走其他的道路,那我就从入口函数开始,也就是我们熟悉的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的对象,他们的关系图如下;


应用最广的模式-单例模式(结合Android源码)_第4张图片
@62[RUXWSUUF5}IJ86]_GL8.png

接着调用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)这个方法,

ActivityStackSupervisor

boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            ArrayList 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 pendingResults, List 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> SYSTEM_SERVICE_FETCHERS =new HashMap>();

原来就是HashMap存贮,也就是我上边写的最后一种单列方式容器存贮,其实写到这里还并没有写完了,既然我们是直接获取的也并没有自己进行注入,那么你想过没有那么到底系统是啥时候给我们注入的了,带这个问题,我们在翻翻源码,瞧瞧,别怕,有我在嘿嘿.....继续接着撸起;

SystemServiceRegistry类:

我们看看这个HashMap到底是啥时候注入的只关心这个map集合就好了,搜索一下;

/**
     * Statically registers a system service with the context.
     * This method must be called during static initialization only.
    注释还是注释写的真的太清楚了,虽然我英语没过好多级,这些还是一看就明白的
     */
    private static  void registerService(String serviceName, Class serviceClass,
            ServiceFetcher serviceFetcher) {
   .........
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }

原来在这地方调用,那到底是啥时候调用的这个registerService还是需要搜索一下,这就比之前简单多了,

static {
        registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
                new CachedServiceFetcher() {
            @Override
            public AccessibilityManager createService(ContextImpl ctx) {
                return AccessibilityManager.getInstance(ctx);
            }});

        registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
                new CachedServiceFetcher() {
            @Override
            public CaptioningManager createService(ContextImpl ctx) {
                return new CaptioningManager(ctx);
            }});

   ......省略
      重点在这,这不是就是我们获取的东西吗;
        registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
..省略..........

总结:

原来我们app已启动的时候下边已经多了大量的工作,在第一次加载类的时候就会注册各种ServiceFetcher,将这些以键值对的形式存进去,需要用到的时候在通过key去取值,到此现在这个流程基本上明白了,那我就用一个流程图来再一次的回头整理下图,只是贴出了一些重要的方法以便回顾之前看的;


应用最广的模式-单例模式(结合Android源码)_第5张图片
启动流程图.png

其实分析了这么多的源码,说到底就是一个核心原理就是构造私有,并且通过静态方法获取一个实例,在这个过程中必须保证线程的安全性;如果觉得写的不错的给个赞哦,写的有问题的地方希望大家能给你纠正,谢谢!

你可能感兴趣的:(应用最广的模式-单例模式(结合Android源码))