Android Framework Design Model (5) -- Singleton Method

Source: Internet
Author: User

Android Framework Design Model (5) -- Singleton Method
I. Introduction to singleton Mode

What is Singleton mode?

In Singleton mode, the object only has one instance and only one instance, regardless of whether it is a single thread or multiple threads, no copy (it takes time and space resources to create a copy ). Its core structure only contains a special class called Singleton. The Singleton mode ensures that there is only one instance in a class in the system and the instance is easy to access, so as to conveniently control the number of instances and save system resources. If you want only one class object to exist in the system and the object needs to coordinate the overall behavior of the system, the singleton mode is the best solution. The Singleton mode is equivalent to a system with only one entry, so that all objects that want to obtain the system resources must go through this entry.

For single-threaded applications or multi-threaded concurrent applications, the singleton mode is the same, but in the case of multi-threaded concurrency, synchronization management is required for the implementation of the singleton mode.

UML diagram of Singleton Mode

Application scenarios of Singleton Mode

Make sure that a class has only one object to avoid generating multiple objects that consume too much resources (time, space), or a certain type of object should only have one special case. At the same time, the singleton mode can reflect the idea of sharing and synchronization. Because Singleton means global, and global means sharing, it needs to coordinate the overall behavior of the system. Therefore, the singleton mode is often inseparable from synchronization.

In general, the singleton mode can be used for the following three requirements:
(1) consistency must be ensured, that is, the result of each intermittent operation is retained, and the next operation continues from the previous intermittent point (I/O stream, database links, buffers, etc ).

(2) There can be only one logical instance (for example, a specific animal is unique)

(3) Frequent creation and use, and high resource consumption during creation. (I/O Stream, DATABASE LINK)

For example:

I/O Stream operations
For I/O Stream operations, the creation process is complex and consumes a lot of resources. The input stream is only a character stream channel. For a file stream, multiple objects have no practical significance, and it must ensure consistency. Multiple objects only increase redundancy and overhead. For example, if you create an input stream for a file, the task of the input stream is to read the file from the external storage into the memory. If you have read the file, close the input stream and re-open the input stream, at this time, if a new input stream is obtained, because it is another input stream object, its pointer and other information are inconsistent with the previous one, and everything will start again. If you want to keep him consistent, you need to spend more time copying the previous information to the existing object. Therefore, it is better to simply use the singleton mode.

Database Connection
For databases, an application or even multiple applications can correspond to only one database. At the same time, for database access, a connection pool management and synchronization management are required to limit the number of connections and data synchronization. If a new database connection object is created, the link information of the currently created object is inconsistent with the previous information, causing program risks or crashes (if consistent, then you need to re-initialize the resource of this object ). We also need to ensure consistency. Therefore, the singleton can restrict the program to implement a connection pool and synchronize the mechanism, saving resources and time.

The creation of the above two objects requires both memory and time consumption, and because they need to ensure consistency between the front and back, there should be only one instance.

II. Implementation of Singleton Mode

The Singleton mode has the following key points:
(1) The constructor is not open to the outside, generally private
(2) return the singleton object through a static method or enumeration
(3) ensure that there is only one single-instance Class Object, especially in a multi-threaded environment.
(4) ensure that the singleton class object will not be re-built during deserialization

In the preceding key points, (1) and (2) are easy to implement, and (3) and (4) are troublesome. Many Singleton mode implementations are under a single thread, synchronization is required for the singleton mode under multiple threads, and deserialization is involved when an object needs to be carved to a disk for storage. Therefore, the singleton mode requires special processing when (3) and (4) are used. The following describes several ways to implement the singleton mode. Some of them are thread-insecure and some are thread-safe. For deserialization and refactoring objects, only enumeration can be prevented.

Note: For deserialization, the system provides a way for programmers to control the deserialization of objects. Therefore, we can overwrite this method for deserialization.

Several implementation methods Lazy 1 (thread unsafe)
public class Singleton {   private static Singleton instance;       private Singleton (){}        public static Singleton getInstance() {       if (instance == null) {           instance = new Singleton();       }       return instance;        }   }  

This method cannot implement Singleton in a multi-threaded environment, and cannot prevent deserialization from re-constructing objects.
Advantage: it is the simplest. It is called lazy because it delays the initialization of a singleton to the first call.
GetInstance method.
Disadvantage: thread security

Lazy 2 (thread security)
 public class Singleton {        private static Singleton instance;        private Singleton (){}      public static synchronized Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;        }   }  

Use the synchronized keyword to modify the getInstance method. This ensures the uniqueness of the single-instance object in general. However, another problem occurs: even if a singleton has been created, each call to the getInstance method will still be synchronized (synchronous Resource Competition processing ). This wastes unnecessary resources and reduces the performance of the singleton mode to save resources. This is a major drawback of the lazy mode (thread security.

Advantage: thread security
Disadvantage: 99% of synchronization is mostly the same

Double check lock (thread security)
Public class Singleton1 {private static Singleton1 instance = null; private static Object synObj = new Object (); private Singleton1 () {} public static Singleton1 getInstance () {if (instance = null) {// first determine whether synchronized (synObj) has been instantiated {// if not instantiated, request to lock if (instance = null) {// After the lock is obtained, determine whether the object has been instantiated by the previous lock. return instance = new Singleton1 () ;}} return instance ;}}

The dual checking lock avoids the disadvantages of the lazy model. It first checks whether there are instances, then competes for the lock, and then judges again after the lock, in this way, the lock competition can be avoided when the object has been instantiated.
Advantage: The instance is unique and thread-safe.
Disadvantages: JDK1.6 or above; loading is slow for the first time; occasional failures (lock failure for double check)

Ele. Me (static member variable)
public class Singleton {        private static Singleton instance = new Singleton();        private Singleton (){}      public static Singleton getInstance() {        return instance;       }   }

The single-instance mode is implemented by using the feature that only creates static member variables once. This feature is managed by classLoader and ensures that the member is initialized only once throughout the whole world.

Advantage: the code is concise and no synchronization lock is required

Disadvantage: it is called a hungry guy because the compiler will initialize the singleton as soon as it sees this class and cannot
The purpose of delayed loading.

Static internal class
package designmodle.singleton;/** * @author David * */public class Singleton3 {   private static class SingletonHolder{       private static final Singleton3 instance = new Singleton3();   }   private Singleton3(){}   public static Singleton3 getInstance(){       return SingletonHolder.instance;   }}

Similar to the hungry guy (static member variable) method, classLoader mechanism is used. The better thing is that it can delay loading. When it loads the Singleton class for the first time, it does not initialize the instance, the SingleHolder is loaded and the instance is initialized only when the getInstance method is called for the first time.

Advantages: thread security, object uniqueness, and delayed instantiation

Disadvantage: None

Enumeration (thread-safe and anti-deserialization)
public enum Singleton {       INSTANCE;       public void whateverMethod() {       }   }  

It's so easy! Enumeration is the simplest way to implement the singleton mode. The most important thing is that enumeration is thread-safe by default, and it can also prevent deserialization!
Enumeration is rarely used, but it is quite simple! It fully complies with all the key elements of the singleton mode.
Advantages: unique instance, thread security, anti-serialization refactoring, simple, simple!

Disadvantage: JDK or above

Container implementation Singleton Mode

If there are many Singleton categories in the program, a container category may be required for management. Therefore, we can also implement the singleton mode of multiple types through containers, at the same time, containers can unify the single-instance interface and reduce coupling.

Public class SingletonManager {private static Map
  
   
ObjMap = new HashMap
   
    
(); Private SingletonManager () {} public static void registerService (String key, Object instance) {// you can check whether this single-column Object is included. if not, add if (! ObjMap. containsKey (key) {objMap. put (key, instance) ;}} public static Object getService (String key) {return objMap. get (key );}}
   
  

This method allows us to manage multiple types of Singleton instances and use a unified interface to perform acquisition operations, reducing user costs, it also hides the specific implementation of getting a singleton from the user (you do not need to know the class name and method of the singleton), reducing the coupling between the user and the singleton class used.

Advantages: The Singleton is unique and thread-safe (the same Singleton is obtained in multi-thread scenarios, and HashMap itself is not thread-safe). The Singleton category is managed, reduce the coupling between the singleton category and the user.

Disadvantage: None

How to Prevent deserialization and re-object Reconstruction

How to prevent problems caused by deserialization is actually very simple. Deserialization allows you to create a new instance of a class in special ways regardless of the constructor visibility of the class. However, the system provides a very special hook Method for developers to control the deserialization of objects. This method is: readResolve (), in this method, we can prevent the singleton object from re-generating the object during back-deserialization.

The method for adding a static internal class is as follows:

Public class Singleton3 implements Serializable {private static class SingletonHolder {private static final Singleton3 instance = new Singleton3 ();} private Singleton3 () {} public static Singleton3 getInstance () {return SingletonHolder. instance;} // Add this hook function. The system will use this Hook method to obtain the original Singleton in the deserialization process. // instead of re-creating a singleton. Private Object readResolve () throws ObjectStreamException {return SingletonHolder. instance ;}}
Summary of implementation methods

Regardless of the form to implement the singleton mode, their core principle is to privatize the constructor and obtain a unique instance through the static method, to ensure thread security during the acquisition process and prevent re-generation of objects caused by deserialization, the method to choose depends on the project.
(1) Whether it is a complex concurrent Environment
(2) Whether the JDK version is too low
(3) resource consumption of a singleton object

Iii. Examples of single-instance mode in Android

In the Android system, we often obtain system-level services through Context, such as WindowsManagerService and ActivityManagerService. It is more commonly used as a LayoutInflater class, these services will be registered in the system as a singleton at the time of registration, and will be obtained through getSystemService (String name) of Context as needed, we can see that in Android, containers are used to manage Singleton of multiple services. The following uses LayoutInflater as an example to describe how to use LayoutInflater in the getView () Policy method of ListView:

LayoutInflater
@ Overridepublic View getView (int position, View convertView, ViewGroup parent) {ViewHolder holder = null; if (null = convertView) {holder = new ViewHolder (); convertView = LayoutInflater. from (mContext ). inflate (mLayoutId, null); // Code omitted} else {// Code omitted} // Code omitted return convertView ;}

We use LayoutInflater. from (Context) to obtain the LayoutInflater service. Let's take a look at the implementation of LayoutInflater. from (Context:

Public static LayoutInflater from (Context context) {// obtain the system's LayoutInflater service LayoutInflater = (LayoutInflater) context. getSystemService (Context. LAYOUT_INFLATER_SERVICE); if (LayoutInflater = null) {throw new AssertionError ("LayoutInflater not found. ");} return LayoutInflater ;}

From the code above, we can see that the from (Context) method calls the getSystemService (String key) method of the Context class. Context is an abstract class, so there must be its implementation class in Android to operate Context. There will be a Context object in Application, Activity, and Service, that is, the total number of Context is: Activity count + Service count + 1 (Application ). ListView is displayed in the Activity, so we can check the main function of Activity entry ActivityThread to track the creation of Activity, we can find that a ContextImpl object is created in the function in the handleBindApplication method, and this object is the implementation class of Context.

Public Application makeApplication (boolean forceDefaultAppClass, Instrumentation instrumentation) {if (mApplication! = Null) {return mApplication;} Application app = null; String appClass = mApplicationInfo. className; if (forceDefaultAppClass | (appClass = null) {appClass = "android. app. application ";} try {java. lang. classLoader cl = getClassLoader (); // you have created the ContextImpl class ContextImpl appContext = new ContextImpl (); appContext. init (this, null, mActivityThread); app = mActivityThread. mInstrumentation. newApplica Tion (cl, appClass, appContext); appContext. setOuterContext (app);} catch (Exception e) {if (! MActivityThread. mInstrumentation. onException (app, e) {throw new RuntimeException ("Unable to instantiate application" + appClass + ":" + e. toString (), e );}}...}

ContextImpl class:

Class ContextImpl extends Context {// code is omitted // ServiceFecher captures samples and obtains various service objects through getService: static class ServiceFecher {// the position of the current service in the container, this facilitates the following synchronization block to obtain the corresponding service int mContextCacheIndex =-1; // gets the system service public Object getService (ContextImpl ctx) {ArrayListCache = ctx. mServiceCache; Object service; // Synchronous lock control synchronized (cache) {if (cache. size () = 0) {for (int I = 0; I <sNextPerContextServiceCacheIndex; I ++) {cache. add (null) ;}} else {// The cache is not empty. Obtain the Service object sercice = cache from the cache. get (mContextCacheIndex); if (service! = Null) {return service ;}// hook method. When the Service in the cache is empty, re-create service = createService (ctx); cache. set (mContextCacheIndex, service); return service ;}/ *** hook method, which is used by subclasses to create service objects */public Object createService (ContextImpl ctx) {throw new RuntimeException ("Not implemented") ;}// Service container, used as the container for storing various single instances: private static final HashMap
   
    
STSTEM_SERVICE_MAP = new HashMap
    
     
(); // Service Record Number pointer, record stored in the next service location of the container private static int sNextPerContextServiceCacheIndex = 0; // register the private static void registerService (String serviceName, ServiceFetcher fetcher) {if (! (Fetcher instanceof StaticServiceFetcher) {// mark the location of the Service in the container fetcher. mContextCacheIndex = sNextPerContextServiceCacheIndex ++;} // Add the Service to the Service container SYSTEM_SERVICE_MAP.put (serviceName, fetcher);} // static statement block, load this class for the first time and execute static {// code omitting // register LayoutInflater Service registerService (LAYOUT_INFLATER_SERVICE, new ServiceFetcher () {// implement the hook method public Object createService (contextimptimpl ctx) {return PolicyManager. make NewLayoutInflater (ctx. getOuterContext ());}});}//........... // Code omitted // get the corresponding service @ Override public Object getSystemService (String name) through the key {// obtain the service selector ServiceFetcher = SYSTEM_SERVICE_MAP.get (name) based on the name ); return fetcher = null? Null: fetcher. getService (this );}}
    
   

From some code of ContextImpl, we can see that ContextImpl is a capacity-based Singleton mode. When the VM loads the class for the first time, it registers various ServiceFetcher types, including The LayoutInflater Service. These services are stored in a HashMap as key-value pairs. You only need to obtain the corresponding ServiceFetcher based on the key when using the service, then, the getService function of the ServiceFetcher object is used to obtain the specific service object. When it is obtained for the first time, the createService function of the hook method is called to create the service object, then, the object is cached in a list and retrieved from the cache next time. This avoids repeated object creation and achieves the effect of a single column. In this mode, the system's core services exist in the form of a single instance, reducing resource consumption.

Iv. Summary

The Singleton model is one of the most widely used models and is also the design model that many people initially come to use. When our system only needs a global object, we can use it to save system resources and coordinate the overall behavior of the system. When applying this mode, pay attention to the four key points mentioned: constructor privatization, a Singleton, multi-thread security, and prevention of deserialization and reconstruction of objects.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.