This article describes the application of singleton mode in Android development. Share it for your reference, as follows:
The singleton pattern is one of the most widely used design patterns. When applying this pattern, the class of the singleton object must ensure that only one instance exists. Many times, the entire system only needs to have one global object, which is conducive to coordinating the overall behavior of the system. For example, in an application, there should be only an ImageLoader instance, which also contains network requests, cache systems, thread pools, etc., which consumes resources, so there is no reason to let it construct multiple instances. This situation where objects cannot be constructed freely is a scenario where singleton pattern is used. There are many such scenarios in the Android system, such as the most commonly used one()
,()
All are singleton modes used. Below is a list of several singleton patterns to build and the advantages and disadvantages of various methods.
1. Lazy mode
The lazy mode declares a static variable and is initialized when the user calls getInstance for the first time. The lazy mode is implemented as follows:
public class Singleton { private static Singleton sInstance; private Singleton(){ } public static synchronized Singleton getInstance(){ if(sInstance == null){ sInstance = new Singleton(); } return sIntance; } }
getIntance()
The synchronized keyword is added to the method, that is, getInstance is a synchronization method, which is the method mentioned above to ensure the uniqueness of singleton objects in multi-threading situation. However, there is a problem with this. Even if sInstance has been initialized, each call to getInstance will be synchronized, which will consume unnecessary resources. This is also the biggest problem with the lazy singleton pattern. The biggest advantage of the lazy mode is that the singleton will only be instantiated when used, which saves resources to a certain extent; the disadvantage is that it needs to be initialized in time when loading for the first time, and the reaction is slightly slower. The biggest problem is that each call getInstance is synchronized, causing unnecessary synchronization overhead. Generally not recommended.
Check Lock(DCL) implements a single case
The advantage of implementing singleton mode in DCL is that it can not only initialize singletons when needed and ensure thread safety. Initialization of singleton objects is called without synchronization locking. Implementation is as follows:
public class Singleton { private static Singleton sInstance; private Singleton(){} public static Singleton getInstance(){ if(sInstance == null){ synchronized(){ if(sInstance == null){ sInstance = new Singleton(); } } } return sInstance; } }
The highlight of this program isgetInstance()
In the method, sIntance is made twice non-empty judgment: the first layer is mainly to avoid unnecessary synchronization, and the second layer is to create an instance in the case of null. Assume that thread A executes tosInstance = new Singleton()
The statement of , which seems to be an atomic operation, but it is not, this code will be compiled into multiple assembly instructions: allocate memory to the Singleton instance, call the Singleton constructor, initialize the member fields, and point the sInstance object to the allocated memory space (the sInstance is no longer null at this time). However, since the Java compiler allows the processor to execute indisorder, the execution order of the above three steps cannot be guaranteed. This will cause DCL to fail, and this kind of error that is difficult to track and reproduce will be hidden for a long time. After JDK1.5, you only need to change the definition of sInstance toprivate volatile static Singleton sInstance = null
This ensures that the sInstance object is read from memory every time, and the singleton mode can be completed using DCL writing. Of course, volatile will more or less affect performance, but considering the correctness of the program, it is still worth it. The advantage of DCL is that its resource utilization is high, and the singleton will be instantiated only when the getInstance is executed for the first time, which is highly efficient. The disadvantage is that the response is slightly slower when loading for the first time, and it also fails occasionally due to the Java memory model. There are also certain defects in high concurrency environments, although the probability of occurrence is small. DCL mode is the most used singleton implementation method. It can instantiate singletons only when needed and ensure the uniqueness of singleton objects in most scenarios. Unless your code is used in concurrent scenarios with more complex or lower than JDK6, this method will definitely meet the requirements.
3. Static internal class singleton pattern
Although DCL solves problems such as resource consumption, unnecessary synchronization, and thread safety to a certain extent, it still fails in some cases. This problem is called DCL double lock failure. The static internal class implementation code is as follows:
public class Singleton{ private Singleton(){} public static Singleton getInstance(){ return ; } private static class SingletonHolder { private static final Singleton sInstance = new Singleton(); } }
When the Singleton class is loaded for the first time, sInstance will not be initialized. Only when Singleton's getS method is called the first time, sIn will be initialized. Therefore, the first call to the get method will cause the virtual machine to load the SingletonHolder class. This method not only ensures that it can be thread-safe, but also ensures that the uniqueness of the singleton object is also delayed.
4. Enumerate singleton mode files
Since Java 1.5 version, single element enumeration has become the best method to implement singleton mode, and the implementation code is as follows
class Resource{ } public enum SomeThing { INSTANCE; private Resource instance; SomeThing() { instance = new Resource(); } public Resource getInstance() { return instance; } }
The above class Resource is the resource we want to apply the singleton pattern, which can be represented as network connections, database connections, thread pools, etc.
The way to obtain resources is simple, just()
You can obtain the desired instance. Let's take a look at how singletons are guaranteed:
First, in the enumeration, we have made it clear that the constructor is limited to private. When we access the enum instance, the constructor will be executed. At the same time, each enum instance is of static final type, which means that it can only be instantiated once. When the constructor is called, our singleton is instantiated.
That is, because the instances in enum are guaranteed to be instantiated only once, our INSTANCE is also guaranteed to be instantiated once.
It can be seen that enum implementation singletons is relatively simple. In addition, let’s take a look at the declaration of the Enum class:
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable
As you can see, enumeration also provides a serialization mechanism. In some cases, such as if we want to transfer a handle to a database connection over the network, it will provide a lot of help.
Among the above-mentioned singleton patterns, there is a situation where they create objects repeatedly, that is, deserialization. By serializing, you can write an instance object of a singleton to disk and then read it back, thereby effectively obtaining an instance. Even if the constructor is private, you can still create a new instance of the class through special ways during deserialization, which is equivalent to calling the private constructor of the class. The deserialization operation provides a very special hook function, the class has a private, instantiated method readResolve, which allows developers to control the deserialization of objects. In the above examples, if you want to prevent the object from being deserialized and regenerated, you need to add the following method:
private Object readResolve() throws ObjectStreamException{ return sInstance; }
That is,readResolve()
Lieutenant GeneralsInstance
Object returns, instead of regenerating a new object by default. For enumerations, this problem does not exist, because even deserialization will not generate new instances.
5. Use containers to implement singleton mode
Take a look at this implementation:
public class SingletonManager { private static Map<String,Object> objMap = new HashMap<String,Object>(); private SingletonManager(); public static viud registerServuce(String key,Object instance){ if(!(key){ (key,instance); } } public static Object getService(String key){ return (key); } }
When the program is initialized, multiple singleton types are injected into a unified management class. When using it, the exclusive type of the object is obtained according to the Key. This method allows us to manage multiple types of singletons, and when using it, we can obtain operations through a unified interface, which reduces the user's usage cost, hides specific implementations from the user, and reduces the degree of coupling.
Summarize:
No matter which way the singleton pattern is implemented, their core principle is to privatize the constructor and obtain a unique instance through a static method. In the process of obtaining this, it is necessary to ensure thread safety and prevent deserialization from regenerating the instance object. Which method to choose depends on the project itself, such as whether it is a complex concurrent environment, the JDK version is too low, the resource consumption of singleton objects, etc.
For more information about Android related content, please check out the topic of this site:Android development introduction and advanced tutorial》、《Android debugging skills and solutions to common problems》、《Summary of the usage of basic Android components》、《Android View View Tips Summary》、《Android layout layout tips summary"and"Android control usage summary》
I hope this article will be helpful to everyone's Android programming design.