Android design pattern singleton mode

Android design pattern singleton mode

People often ask me how to advance in Android learning? No matter how you go, design patterns are essential for advancement. Understanding and applying design patterns will be of great help to your subsequent code writing and architecture design. So starting today, I will take some time to share with you the design pattern series.

[[164566]]

What is a design pattern? In fact, a simple understanding is that it is just a summary of some experiences left by our predecessors, and then these experiences are named Design Pattern, which means design pattern. By using design patterns, we can make our code more reusable, more maintainable, and make your code more elegant. In theory, there are 23 design patterns, but I will only share some commonly used design patterns on the Android platform. Today, I will share the most commonly used singleton pattern.
Hungry Man

  1. public class Singleton {
  2.  
  3. private static Singleton instance = new Singleton();
  4.  
  5. private Singleton(){}
  6.  
  7. public static Singleton newInstance(){
  8. return instance;
  9. }
  10. }

The hungry approach is the simplest implementation method. This approach is suitable for situations where a singleton is required during initialization. This approach is simple and crude. If the singleton object is initialized very quickly and occupies very little memory, this approach is more appropriate. It can be directly loaded and initialized when the application starts. However, if the singleton initialization operation takes a long time and the application has requirements for startup speed, or the singleton occupies a large amount of memory, or the singleton is only used in a certain scenario and is not used under normal circumstances, the hungry singleton mode is not appropriate. At this time, the lazy approach is needed to delay the loading of the singleton on demand.

Lazy

  1. public class Singleton {
  2. private static Singleton instance = null ;
  3.  
  4. private Singleton(){}
  5.  
  6. public static newInstance(){
  7. if( null == instance){
  8.              instance = new Singleton();
  9. }
  10. return instance;
  11. }
  12. }

The biggest difference between the lazy approach and the hungry approach is that the initialization of the singleton is delayed until it is needed, which is very useful in some situations. For example, if a singleton is not used very often, but the functions it provides are very complex, and loading and initializing it consumes a lot of resources, then the lazy approach is a very good choice.
Singleton mode under multithreading

Some basic application methods of the singleton pattern are introduced above, but the usage methods mentioned above all have an implicit premise, that is, they are all applied under single-threaded conditions. Once they are switched to multi-threaded conditions, there is a risk of errors.

If there are multiple threads, the hungry approach will not cause any problems, because the JVM will only load the singleton class once, but the lazy approach may cause the problem of creating the singleton object repeatedly. Why is there such a problem? Because the lazy approach is thread-unsafe when creating a singleton, multiple threads may call its newInstance method concurrently, resulting in multiple threads creating multiple copies of the same singleton.

Is there any way to make the lazy single-interest model thread-safe? The answer is definitely yes, that is, to use a synchronous lock to achieve it.
Lazy Sync Lock

  1. public class Singleton {
  2.    
  3. private static Singleton instance = null ;
  4.    
  5. private Singleton(){
  6. }
  7.    
  8. public static Singleton getInstance() {
  9. synchronized (Singleton.class) {
  10. if ( instance == null) {
  11.                  instance = new Singleton();
  12. }
  13. }
  14.    
  15. return instance;
  16. }
  17. }

This is the most common way to solve synchronization problems. Use synchronized lock (Singleton.class) to prevent multiple threads from entering at the same time and causing the instance to be instantiated multiple times. Here is an example of using this method in Android:
InputMethodManager Example

  1. public final class InputMethodManager {
  2. //Internal global unique instance
  3. static InputMethodManager sInstance;
  4.      
  5. //External API
  6. public static InputMethodManager getInstance() {
  7. synchronized (InputMethodManager.class) {
  8. if ( sInstance == null) {
  9. IBinder b = ServiceManager .getService(Context.INPUT_METHOD_SERVICE);
  10. IInputMethodManager service = IInputMethodManager .Stub.asInterface(b);
  11.                  sInstance = new InputMethodManager(service, Looper.getMainLooper());
  12. }
  13. return sInstance;
  14. }
  15. }
  16. }

The above is the singleton usage related to the input method class in the Android source code.

But there is actually a better way:
Double check lock

  1.  
  2.  
  3. public class Singleton {
  4.    
  5. private static volatile Singleton instance = null ;
  6.    
  7. private Singleton(){
  8. }
  9.    
  10. public static Singleton getInstance() {
  11. // if already inited, no need to get lock every time
  12. if ( instance == null) {
  13. synchronized (Singleton.class) {
  14. if ( instance == null) {
  15.                      instance = new Singleton();
  16. }
  17. }
  18. }
  19.    
  20. return instance;
  21. }
  22. }

You can see that another layer of if is added outside synchronized (Singleton.class) above. This is to avoid executing synchronized (Singleton.class) to obtain the object lock the next time the instance is instantiated, thereby improving performance.

The above two methods are still quite troublesome. We can't help but ask, is there a better way to achieve it? The answer is yes. We can use the JVM's class loading mechanism to achieve it. In many cases, JVM has provided us with synchronization control, such as:

Data initialized in the static{} block

When accessing final fields

etc.

Because the JVM will ensure that the data is synchronized when loading the class, we can implement it like this:

Use inner classes to create object instances in the inner classes. In this way, as long as the inner class is not used in the application, the JVM will not load the singleton class, and will not create a singleton object, thus achieving lazy delayed loading and thread safety.

The implementation code is as follows:
Static inner class

  1.  
  2.  
  3. public class Singleton {
  4. //Inner class, a single benefit object will be created when the inner class is loaded
  5. private static class SingletonHolder{
  6. public static Singleton instance = new Singleton();
  7. }
  8.  
  9. private Singleton(){}
  10.  
  11. public static Singleton newInstance(){
  12. return SingletonHolder.instance;
  13. }
  14.  
  15. public void doSomething(){
  16. //do something
  17. }
  18. }

The singleton class implemented in this way is thread-safe and very simple to use. Mom no longer has to worry that my singleton is not a singleton.

However, this is not the simplest way. Effective Java recommends a simpler and more convenient way to use it, which is to use enumeration.
Enumeration type singleton pattern

  1. public enum Singleton {
  2. //Define an enumeration element, which is an instance of Singleton
  3. instance;
  4.  
  5. public void doSomething(){
  6. // do something ...
  7. }
  8. }

Here’s how to use it:

  1. public static void main(String[] args){
  2. Singleton singleton = Singleton .instance;
  3. singleton.doSomething();
  4. }

By default, the creation of enumeration instances is thread-safe. (Creating a singleton of an enumeration class is also thread-safe at the JVM level), so there is no need to worry about thread safety issues. In theory, enumeration classes are the easiest way to implement singleton mode.
Summarize

Generally, the singleton pattern includes five ways of writing, namely hungry, lazy, double-checked lock, static inner class and enumeration. I believe that after reading this, you have a full understanding of the singleton pattern. Choose the singleton pattern you like best according to different scenarios!

<<:  Writing command line programs in Swift

>>:  Android-Super simple to achieve picture rounded corners

Recommend

Where is the traffic pool for educational institutions in 2021?

In 2020, affected by the COVID-19 pandemic, the o...

Smart hardware industry leaders give in-depth analysis of Apple Watch

After a period of excitement, the public's di...

The unspoken rules of Baidu’s bidding promotion ranking!

Traffic is the blood that keeps online marketing ...

Is it true that crying can eliminate toxins from the body?

gossip "Shedding tears can expel toxins from...

It’s time for wearable computing to “return to algorithms”

In April 2014, the “big companies” did two things...

Huge amount of Qianchuan short video distribution experience!

The author of this article is Luban Optimizer. Lu...

Life Encyclopedia丨Is it really good to have a full refrigerator?

Almost every household has a refrigerator. Leftov...

"People's hope" has not yet come true: Remdesivir still needs to be verified

The results of remdesivir in treating COVID-19 ar...