单例模式android_android 单例模式样例 - CSDN
  • Android完美单例模式: 以前写的单例模式考虑不完全; 面试的时候,考到这样的问题; 想到这么的问题,居然也会出现,只是后面才发现自己写的单例,太过幼稚; 所以到网上找了一些资料,重新写一个;

    Android完美单例模式:

    以前写的单例模式考虑不完全;

    面试的时候,考到这样的问题;

    想到这么的问题,居然也会出现,只是后面才发现自己写的单例,太过幼稚;

    所以到网上找了一些资料,重新写一个;


    单例模式:

    可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。


    分为:懒汉式单例、饿汉式单例、登记式单例三种;


    懒汉式:用到的时候再加载;

    饿汉式:一开始就加载;

    登记式:使用Map等,登记对象是否实例化;有,则返回对象,无则实例化对象;


    线程安全与否,在于懒汉式是否加上同步,或者是实例化时加锁操作;


    package com.example.demo;
    
    public class HttpUtils {
    	//volatile的作用是: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.
    	//一个定义为volatile的变量是说这变量可能会被意想不到地改变,
    	//这样,编译器就不会去假设这个变量的值了。
    	//精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
    	private volatile static HttpUtils instance;
    	
    	private HttpUtils()
    	{
    		//构造函数 逻辑处理 
    	}
    
    	public static HttpUtils getInstance()
    	{
    		//第一次判断是否为空
    		if(instance==null)
    		{
    			synchronized (HttpUtils.class) {//锁
    				//第二次判断是否为空 多线程同时走到这里的时候,需要这样优化处理
    				if(instance ==null)
    				{
    					instance =new HttpUtils();
    				}
    			}
    		}
    		return instance;
    	}
    }
    

    单例模式,在Android Framework 中运用广泛;

    比如说:

    1、输入法管理  InputMethodManager

    2、View获得点击、焦点、文字改变等事件的分发管理  AccessibilityManager


    public final class InputMethodManager {
        static final boolean DEBUG = false;
        static final String TAG = "InputMethodManager";
    
        static final String PENDING_EVENT_COUNTER = "aq:imm";
    
        static InputMethodManager sInstance;
    
        InputMethodManager(IInputMethodManager service, Looper looper) {
            mService = service;
            mMainLooper = looper;
            mH = new H(looper);
            mIInputContext = new ControlledInputConnectionWrapper(looper,
                    mDummyInputConnection, this);
        }
    
        /**
         * Retrieve the global InputMethodManager instance, creating it if it
         * doesn't already exist.
         * @hide
         */
        public static InputMethodManager getInstance() {
            synchronized (InputMethodManager.class) {
                if (sInstance == null) {
                    IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
                    IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
                    sInstance = new InputMethodManager(service, Looper.getMainLooper());
                }
                return sInstance;
            }
        }
        
        /**
         * Private optimization: retrieve the global InputMethodManager instance,
         * if it exists.
         * @hide
         */
        public static InputMethodManager peekInstance() {
            return sInstance;
        }
    }



     


    展开全文
  • 单例模式Android

    万次阅读 热门讨论 2014-06-08 00:41:11
    单例模式(Singleton)一、 什么是单例模式单例模式,简单点来说就是设计一个类,使其在任何时候,最多只有一个实例,并提供一个访问这个实例的全局访问点。二、 为什么要单例在程序中的很多地方,只有一个实例是...

    单例模式(Singleton)

    一、  什么是单例模式

    单例模式,简单点来说就是设计一个类,使其在任何时候,最多只有一个实例,并提供一个访问这个实例的全局访问点。


    二、  为什么要单例

    在程序中的很多地方,只有一个实例是非常重要的。例如,在windows中,任务管理器只有一个,无论你点击多少次打开任务管理器,任务管理器也只会生成一个窗口。再例如,在一些软件中,工具箱是唯一的,无论你点击多少次打开工具箱,工具箱也只一个。

     

    为什么要这样设计呢?因为像任务管理器或工具箱这样的程序,只要有一个就足够完成所有的工作了,多个程序只会白白消耗系统资源,而像任务管理器这类的程序还会引入多个任务管理器之间的同步问题,所以对些这些程序来说,只有一个实例或程序是必要的。


    三、  为什么需要单例模式

    上面讲到对于某些程序来说,保持其只有一个实例是必要的,但是如何保证一个程序或一个类只有一个实例呢?下面从类的角度来解说。

     

    第一种方法,我们抛开设计模式这个概念,如果你之前完全不知道这个概念,面对这个设计要求你会怎样做?我们可以使用一个全局的类指针变量,初始值为NULL,每当需要创建该类的对象时,都检查该指针是否为NULL,若为NULL,则使用new创建新的对象,并把对象的指针赋值给该全局指针变量。若该指针不为NULL,则直接返回该指针或使用该指针。这个可能是最容易想到的方法。

     

    第二种方法,就是使用单例模式。单例模式通过在类内维护一下指向该类的内部的指针,并把其构造函数声明为private或protected来阻止一般的实例化,而使用一个static的公有成员函数来实现和控制类的实例化。在该static公有成员函数中判断该类的静态成员指针是否为NULL,若为NULL,则创建一个新的实例,并把该类的静态成员指针指向该实现。若该静态成员指针不为NULL,则直接返回NULL。若这里看得不是很明白,不要紧,看了下面的类图和代码自会明白。

     

    相比之下,第二种方法比第一种方法好在哪里呢?首先,第一种做法并没有强制一个类只能有一个实例,一切的控制权其实在使用者的设计中;而第二种做法,则是类的设计者做好的,与使用者并没有关系。换句话来说,如果使用第一种做法,则只要使用者愿意,该类可以有无数个实例,而对于第二种方法,无论使用都是否愿意,它只能有一个实例。这就好比我们去吃饭,第一种方法需要顾客来判断哪些菜已经卖完,而第二种方法由餐馆的判断哪些菜已经卖完,显然在生活中,第二种方法才是合理的。


    四、  单例模式的类图



    五、  单例模式的实现(C++实现)

    1singleton.h,定义类的基本成员及接口

    #ifndef SINGLETON_H_INCLUDE
    #define SINGLETON_H_INCLUDE
     
    class Singleton
    {
        public:
           static Singleton*getInstance();
           voidreleaseInstance();
     
        private://function
           Singleton(){}
           ~Singleton(){}
     
        private://data
           static Singleton*_instance;
           static unsigned int_used;
    };
    #endif

    2singleton.cpp,实现getInstance方法和releaseInstance方法

    #include "singleton.h"
     
    Singleton* Singleton::_instance(0);
    unsigned int Singleton::_used(0);
     
    Singleton* Singleton::getInstance()
    {
        ++_used;
        if(_instance == 0)
        {
           _instance = newSingleton();
        }
        return _instance;
    }
     
    void Singleton::releaseInstance()
    {
        --_used;
        if(_used == 0)
        {
           delete _instance;
           _instance = 0;
        }
    }
    

    代码分析:

    从上面的类图和代码实现可以看到,在单例模式中,我们把类的构造函数声明为私有的,从而阻止了在类外实例化对象,既然在类外不能实例化对象,那么我们如何实例化该类呢?我们知道static成员是随类而存在的,并不随对象而存在,所以我们利用一个公有的static函数,由它来负责实现化该类的对象,因为该static函数是该类的成员函数,它可以访问该类的private的构造函数,它也就是我们之前所说的全局访问点。


    由于可能有多个对象都引用该单例类的对象,而该对象只有一个,所以肯定会有多个指针变量指向堆中同一块内存,若其中一个指针把该堆内存delete掉,然而其他的指针并不知道它所引用的对象已经不存在,继续引用该对象必然会发生段错误,为了防止在类的外部调用delete,在这里把析构函数声明为private,从而让在类外delete一个指向该单例类对象指针的操作非法。


    但是C++的堆内存完全由程序员来管理,如果不能delete的话,该对象就会在堆内存中一直存在,所以在此引入了一个方法releaseInstance和引用计数,当不再需要使用该对象时调用releaseInstance方法,该方法会把引用计数减1,当所有代码都不需要使用该对象时释放该对象,即当引用计数为0时,释放该对象。


    六、  多线程下的单例模式

    上面的代码在多线程环境下会引发问题,举个例子,就是当两个线程同时调用getInstance函数时,若该类还没有被实例化,则两个线程读取到的_instance为0,那么两个线程都会new一个新的对象,从而让该类有两个实例,同时,对_used的操作也会存在相同的问题,此时_use为1。所以,显然该设计在多线程下是不安全的。

     

    为了解决上述问题,我们需要为函数getInstance和releaseInstance中对_instance和_used的访问加锁。为了简便,只列出部分关键代码,修改后的代码如下所示:(源代码文件为singlton_thread.h和singleton_thread.cpp)

    pthread_mutex_tSingleton::_mutex(PTHREAD_MUTEX_INITIALIZER);
     
    Singleton* Singleton::getInstance()
    {
        pthread_mutex_lock(&_mutex);
        ++_used;
        if(_instance== 0)
        {
           _instance= new Singleton();
        }
        pthread_mutex_unlock(&_mutex);
        return_instance;
    }
     
    void Singleton::releaseInstance()
    {
        pthread_mutex_lock(&_mutex);
        --_used;
        if(_used== 0)
        {
           delete_instance;
           _instance= 0;
        }
        pthread_mutex_unlock(&_mutex);
    }

    代码分析:

    从上面的代码可以看出,每次申请调用get/releaseInstance函数都会加锁和解锁,而加锁和解锁都是比较耗时的操作,所以上述的代码效率其实并不高。

     

    在一些设计模式的书上,会看到使用双if的判断来解决多次上锁的问题,但是这个方法在这里是不现实的,因为这个方法不能解决_used的访问问题,也就是说,即使对_instance的访问可以使用双if语句来大大减少加锁和解锁的操作,但是对_used的++和--操作同样需要加锁进行。而那些书上之所以可以使用双if来解决这个问题,是由所使用的语言决定的,例如使用java或c#,它们不需要对内存进行管理,所以不会存在上面代码中所出现的引用计数_used,所以双if的方法才行得通。

     

    若想在C++中实现双if的判断,则不使用引用计数来管理内存即可,即对象一旦分配就一直存在于堆内存中。此时C++也不存在引用计数问题,不需要释放内存,因而也就不需要上面的releaseInstance方法。其getInstance方法的实现如下:

    Singleton* Singleton::getInstance()
    {
    	if(_instance == 0)
    	{
    		pthread_mutex_lock(&_mutex);
    		if(_instance == 0)
    			_instance = new Singleton();
    		pthread_mutex_unlock(&_mutex);
    	}
    	return _instance;
    }


    这样就可以只加锁和解锁一次,大大提高时间效率。但是对象一旦分配内存,内存就不会被释放。所以在C++中使用哪种实现策略,取决于你对时间和空间的取舍,若时间更重要,则采用后一种方法,若空间更重要,则采用前一种方法。


    七、  Android中的单例模式

    Android中存在着大量的单例类,如:InputMethodManager类,CalendarDatabaseHelper类、Editable类等等。在这些类中,都存在一个方法getInstance,在该方法或直接返回对象的引用或判断一个类的引用是否为NULL,若不为NULL,则直接返回该引用,若为NULL,则new一个新的对象,并返回。例如,对于CalendarDatabaseHelper类,存在如下的代码:

    public static synchronized CalendarDatabaseHelper getInstance(Contextcontext)
    {
        if (sSingleton == null)
        {
           sSingleton = newCalendarDatabaseHelper(context);
        }
            return sSingleton;
    }

    从这里的代码可以看出,其实现方式与上面所说的非常相似,不过因为java不用程序员自己管理内存,所以并不需要使用引用计数,而该方法是公有static的。而synchronized就是为了保证同一时刻只能有一个线程进入该方法,这也就是防止上面第六点讲到的单例模式在多线程中的安全问题。


    八、  源代码地址

    C++源代码地址:http://download.csdn.net/detail/ljianhui/7464147


    展开全文
  • 单例模式是应用最广的设计模式之一,在应用这种模式的时候,单例对象的类必须保证只有一个实例存在。许多时候,整个系统只需要拥有一个全局对象,这样有利于协调系统的整体行为。如一个应用中,应该只有ImageLoader...

    单例模式是应用最广的设计模式之一,在应用这种模式的时候,单例对象的类必须保证只有一个实例存在。许多时候,整个系统只需要拥有一个全局对象,这样有利于协调系统的整体行为。如一个应用中,应该只有ImageLoader实例,这个ImageLoader实例中又包含网络请求、缓存系统、线程池等,很耗资源,因此没有理由让他构造多个实例。这种不能自由构造对象的情况就是使用单例模式的场景。在Android系统中存在很多这种场景,比如最常用的context.getSystemService(),BluetoothAdapter.getDefaultAdapter()等等都是使用的单例模式。下面就列出几种单例模式的构建方式以及各种方式的优缺点。

    1.懒汉模式

    懒汉模式是申明一个静态变量,并且在用户第一次调用getInstance时进行初始化,懒汉模式实现如下:

    public class Singleton {
    	private static Singleton sInstance;
    	private Singleton(){
    	}
    	public static synchronized Singleton getInstance(){
    		if(sInstance == null){
    			sInstance = new Singleton();
    		}
    		return sIntance;
    	}
    }

    
    

    getIntance()方法中添加了synchronized关键字,也就是getInstance是一个同步方法,这就是上面所说的在多线程情况下保证单例对象唯一性的手段。但是这样存在一个问题,即使sInstance已经被初始化,每次调用getInstance都会进行同步,这样会消耗不必要的资源,这也是懒汉单例模式存在的最大问题。懒汉模式的最大优点是单例只有在使用的时候才会被实例化,在一定程度上节约了资源;缺点是第一次加载时需要及时初始化,反应稍慢,最大的问题是每次调用getInstance都进行同步,造成不必要的同步开销。一般不建议使用。

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

    DCL方式实现单例模式的优点是既能够在需要的时候才初始化单例又能保证线程安全,且单例对象的初始化后调用getInstance不进行同步锁。实现如下:

    public class Singleton {
        private static Singleton sInstance;
        private Singleton(){}
        public static Singleton getInstance(){
            if(sInstance == null){
                  synchronized(Singleton.class){
    			if(sInstance == null){
    				sInstance = new Singleton();
    			}
    		}
            }
    	return sInstance;
        }
    }

    本程序的亮点在于getInstance()方法中对sIntance进行了两次非空判断:第一层主要是为了避免不必要的同步,第二层的判断则是为了在null的情况下创建实例。假设线程A执行到sInstance = new Singleton()的语句,这看起来是原子操作,但并不是,这句代码会被编译成多条汇编指令:给Singleton的实例分配内存,调用Singleton的构造函数,初始化成员字段,将sInstance对象指向分配的内存空间(此时的sInstance已经不是null了)。但是由于Java编译器允许处理器乱序执行,所以上述三个步骤的执行顺序无法得到保证。这就会导致DCL失效,而且这种难以跟踪重现的错误会隐藏很久。在JDK1.5后,只需要将sInstance的定义改成private volatile static Singleton sInstance = null就可以保证sInstance对象每次都是从内存中读取,就可以使用DCL的写法来完成单例模式。当然,volatile或多或少会影响到性能,但考虑到程序的正确性,还是值得的。DCL的优点是资源利用率高,第一次执行getInstance时单例才会被实例化,效率高。缺点是第一次加载时反应稍慢,也由于Java内存模型的原因会偶尔失败。在高并发环境下也有一定的缺陷,虽然发生概率较小。DCL模式是使用最多的单例实现方式,它能够在需要时才实例化单例,并且在绝大多数场景下保证单例对象的唯一性,除非你的代码在并发场景比较复杂或者低于JDK6的情况下使用,否则这种方式一定能够满足要求。

    3.静态内部类单例模式

    DCL虽然在一定程度上解决了资源消耗、多余的同步、线程安全等问题,但是它还是在某些情况下出现失效的问题。这个问题被才会被称为DCL双锁失效。静态内部类实现代码如下:

    public class Singleton{
       	 private Singleton(){}
       	 public static Singleton getInstance(){
           		 return SingletonHolder.sInstance; 
        	}
    
       	private static class SingletonHolder {
    	private static final Singleton sInstance = new Singleton();
    	}
    
    }
    当第一次加载的时候Singleton类时,并不会初始化sInstance,只有第一次在调用Singleton的getS方法时才会导致sIn被初始化。因此第一次调用get方法会导致虚拟机加载SingletonHolder类,这种方法不但保证能够=线程安全们也能够,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,。

    4.枚举单例模式档
    Java1.5版本起,单元素枚举实现单例模式成为最佳的方法,实现代码如下

    class Resource{
    }
    
    public enum SomeThing {
        INSTANCE;
        private Resource instance;
        SomeThing() {
            instance = new Resource();
        }
        public Resource getInstance() {
            return instance;
        }
    }

    上面的类Resource是我们要应用单例模式的资源,具体可以表现为网络连接,数据库连接,线程池等等。
    获取资源的方式很简单,只要 SomeThing.INSTANCE.getInstance() 即可获得所要实例。下面我们来看看单例是如何被保证的:
    首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法,同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。
    也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的INSTANCE也被保证实例化一次。
    可以看到,枚举实现单例还是比较简单的,除此之外我们再来看一下Enum这个类的声明:

    public abstract class Enum<E extends Enum<E>>
            implements Comparable<E>, Serializable


    可以看到,枚举也提供了序列化机制。某些情况,比如我们要通过网络传输一个数据库连接的句柄,会提供很多帮助。

    在上述的几种单例模式中,存在一种情况它们会出现重复创建对象的情况,那就是反序列化。通过序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效的获得一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的私有构造函数。反序列化操作提供了一个很特别的钩子函数,类中具有一个私有的、被实例化的方法readResolve,这个方法可以让开发人员控制对象的反序列化。上述几个示例中如果要杜绝对象下被反序列化中重新生成对象,那么需要加入如下方法:

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

    也就是在readResolve()中将sInstance对象返回,而不是默认的重新生成一个新的对象。而对于枚举,并不存在这个问题,因为即使反序列化也不会生成新的实例。

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

    看看这种实现方式:

    public class SingletonManager {
    private static Map<String,Object> objMap = new HashMap<String,Object>();
    private SingletonManager();
    public static viud registerServuce(String key,Object instance){
         if(!objMap.containsKey(key){
             objMao.put(key,instance);
    }
    } 
    
    public static Object getService(String key){
        return objMap.get(key);
    }
    }
    
    
    
    
    

    
    

    在程序初始化的时候,将多种单例类型注入到一个统一的管理类中,在使用时根据Key获取对象对应类型的独享,这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体的实现,降低了耦合度。

    总结:不管以哪种方式实现单例模式,它们的核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在获取这个的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题。选择哪种方式取决于项目本身,如是否是复杂的并发环境、JDK版本过低、单例对象的资源消耗等。

    
    

    展开全文
  • Android设计模式之单例模式

    千次阅读 2018-11-04 10:09:33
    单例模式的定义: 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。   实现单例模式的关键点: 1.构造函数不对外开放,一般为private。 2.通过一个静态方法或者枚举返回单例类对象。 3....

    单例模式的定义:

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

     

    实现单例模式的关键点:

    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容器的单例模式实现方式,系统核心服务以单例形式存在,减少了资源消耗。

     

     

    参考书籍:<<Android源码设计模式>>

    展开全文
  • Android单例模式详解

    千次阅读 2018-04-09 22:18:26
    一、前期基础知识储备(1)设计模式定义:Design pattern,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问...
  • 在所有的设计模式中,单例模式是我们在项目开发中最为常见的设计模式之一,而单例模式有很多种实现方式,你是否都了解呢?高并发下如何保证单例模式的线程安全性呢?如何保证序列化后的单例对象在反序列化后任然是...
  • 但很多人对单例模式一知半解,单例模式的写法非常多,不同写法的区别很大,这篇文章的目的是带你深入学习一下单例模式。 什么是单例模式 单例模式是一种对象创建型模式,用来编写一个类,在整个应用系统中只能有该类...
  • Android开发设计模式之——单例模式

    万次阅读 多人点赞 2014-04-03 11:47:06
    比如在android实际APP 开发中用到的 账号信息对象管理, 数据库对象(SQLiteOpenHelper)等都会用到单例模式。下面针对一些例子分析一下我们在开发过程中应用单例模式需要注意的点。 一、作用 单例模式(Singleton...
  • 1、什么是单例模式 单例模式意味着只有一个对象,至于单例模式如何实现就不这里就不多说了,有很多种实现办法。 2、什么时候该使用单例模式?如果不使用单例模式会出现什么问题? 先举2个例子 例子1 ...
  • Android中的单例模式

    2020-08-14 11:48:25
    单例模式是我们在项目开发中用得最多的一种设计模式。单例模式要求:1、单例模式的类在整个应用中只有一个实例对象。2、单例模式的类有一个对外方法提供类的实例。为此我们设计单例模式需要满足以下几点: 1、构造...
  • Android源码中的单例模式android.util.SingletonAndroid系统源码中的单例模式: 代码路径:/frameworks/base/core/java/android/util/Singleton.javapackage android.util; /** * Singleton helper class for lazily...
  • 单例模式介绍及它的使用场景单例模式是应用最广的模式,也是我最先知道的一种设计模式,在深入了解单例模式之前,每当遇到如:getInstance()这样的创建实例的代码时,我都会把它当做一种单例模式的实现。...
  • Android 单例模式

    2018-10-12 14:13:59
    文章目录1、单例模式介绍2、单例模式的简单例子3、懒汉模式实现单例模式4、Double Check Lock (DLC) 实现单例5、静态内部类单例模式6、枚举单例7、使用容器是实现单例模式 1、单例模式介绍 据说在一个app 中,单例...
  • Android中的单例模式使用场景

    千次阅读 2016-08-12 14:37:33
    1.设计模式实例-单例模式 单例模式,故名思议,是指在一个类中通过设置静态使得其仅创造一个唯一的实例。这样设置的目的是满足开发者的希望??这个类只需要被实例化创建一次,同时因为其为静态的缘故,加载的速度也...
  • Android 设计模式 之 单例模式

    万次阅读 多人点赞 2012-10-16 11:34:56
    设计模式中,最简单不过的就是单例模式。先看看单例模式 原文:http://www.iteye.com/topic/575052 Singleton模式可以是很简单的,它的全部只需要一个类就可以完成(看看这章可怜的UML图)。但是如果在“对象创建...
  • Android开发中无处不在的设计模式——单例模式

    千次阅读 多人点赞 2015-10-15 08:53:50
    对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次。而在android开发中,必要的了解一些设计模式又是非常有必要的...这篇文章介绍一个模式,就是单例模式
  • 单例模式的几种实现方式Demo
  • Android 单例模式(懒汉式、饿汉式)

    千次阅读 2017-11-21 13:24:51
    单例模式是设计模式中比较简单的一种。适合于一个类只有一个实例的情况,比如窗口管理器,打印缓冲池和文件系统, 它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一...
  • https://blog.csdn.net/Mars_idea/article/details/80724404 https://blog.csdn.net/cselmu9/article/details/51366946 https://blog.csdn.net/huachao1001/article/details/51536074 ...
  • Androidstudio添加模块代码_单例模式

    千次阅读 2019-12-14 11:17:35
    Androidstudio自带模块代码,这里自定义一个单例模式 具体配置如下: private static $name$ sInstance; public static $name$ get() { if (sInstance == null) { sInstance = new $name$(); } ...
1 2 3 4 5 ... 20
收藏数 34,053
精华内容 13,621
关键字:

单例模式android