Analysis of practical application scenarios of singleton design pattern in Android development – activity management

Analysis of practical application scenarios of singleton design pattern in Android development – ​​activity management

Actual scenarios in Android

Commonly used scenarios are special classes, such as management classes, skin management, and activity management.

1.1 Introduction

The client calls the singleton class.

Idea:

  1. Constructor privatization.
  2. Get the singleton object through static methods. Client call
  3. Ensure that there is only one object in a multi-threaded environment.
  4. Deserialization cannot reconstruct new objects.

1.1.1 Lazy mode

Declare a static object and initialize it on the first call.

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

We add the synchronized keyword so that thread synchronization can be achieved and singletons can be guaranteed in a multi-threaded environment. The lazy method will only be instantiated when it is called for the first time, saving resources. The disadvantage is that the call to the synchronous method causes resource overhead.

1.1.2 DCL singleton

The singleton is instantiated only when needed, ensuring thread safety and no synchronization lock overhead.

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;
    }
}

In the getInstance() method, we test null twice. The first time is to avoid synchronization locking for each call, and the second time is to create an instance object under null conditions.

analyze:

Assume that two threads A and B execute getInstance(), instance = new Singleton(); this code will be translated into multiple assembly instructions,

  1. Allocate memory.
  2. Call the constructor.
  3. Point the reference to the memory space.

In a multi-threaded environment, the execution order of these three steps is different, which will cause DCL to fail. The execution order may be 1, 2, 3, or 1, 3, 2. After 3 is executed and before 2 is executed, switch to thread B. At this time, thread A executes 3 and the instance is not empty. If thread B obtains the instance and it is not empty, an error will occur.

1.1.3 volatile keyword

  1. Prevent reordering.
  2. Thread visibility – a certain thread changes a public object (variable), which may not be visible for a short period of time. Each thread has its own buffer, thread workspace,
public class Singleton {
    
    
    private volatile static Singleton instance;

    private Singleton() {
    
    
    }

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

1.1.4 Static inner classes

The first time the Singleton class is loaded, the object will not be instantiated. The first call to getInstance will cause it to be initialized. The first call to getInstance() will cause the SingletonHolder to be loaded, which can ensure thread safety, unique objects, and delayed instantiation.

public class Singleton {
    
    

    private Singleton() {
    
    
    }

    public static Singleton getInstance() {
    
    
        return SingletonHolder.sInstance;
    }

    private static class SingletonHolder {
    
    
        private static final Singleton sInstance = new Singleton();
    }
}

1.1.5 Container singleton mode

This is used for Android system services

public class Singleton {
    
    
    private static Map<String, Object> mSingleton = new HashMap<>();

    static {
    
    
        mSingleton.put("activity_manager", new Singleton());
    }

    public static Object getService(String serviceName) {
    
    
        return mSingleton.get(serviceName);
    }
}

Android practical application – Activity management

Requirement background: When we have many activities, we need to write a management class to operate their addition, deletion, modification and query.

xml layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.DebugActivity">

    <TextView
        android:onClick="click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text"
        android:textSize="30sp" />


</LinearLayout>

ActivityManager management class

public class ActivityManager {
    
    
    private static ActivityManager mInstance;
    private Stack<Activity> mActivities;

    private ActivityManager() {
    
    
        mActivities = new Stack<>();
    }

    public static ActivityManager getInstance() {
    
    
        if (mInstance == null) {
    
    
            synchronized (ActivityManager.class) {
    
    
                if (mInstance == null) {
    
    
                    mInstance = new ActivityManager();
                }
            }
        }
        return mInstance;
    }

    public void attach(Activity activity) {
    
    
        mActivities.add(activity);
    }

    public void detach(Activity detachActivity) {
    
    
        for (Activity activity : mActivities) {
    
    
            if (activity == detachActivity) {
    
    
                mActivities.remove(activity);
            }
        }
    }

    public void finish(Activity finishActivity) {
    
    
        for (Activity activity : mActivities) {
    
    
            if (activity == finishActivity) {
    
    
                mActivities.remove(activity);
                activity.finish();
            }
        }
    }

    public void finish(Class<? extends Activity> finishClassActivity) {
    
    
        for (Activity activity : mActivities) {
    
    
            if (activity.getClass().getCanonicalName().equals(finishClassActivity.getCanonicalName())) {
    
    
                mActivities.remove(activity);
                activity.finish();
            }
        }
    }
    public Activity currentActivity() {
    
    
        return mActivities.lastElement();
    }
}

LoginActivity

public class LoginActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityManager.getInstance().attach(this);
        setTitle("我是LoginActivity");
    }

    public void click(View view) {
    
    
        Intent intent = new Intent(this, RegisterActivity.class);
        startActivity(intent);
    }

    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        ActivityManager.getInstance().detach(this);
    }
}

RegisterActivity

public class RegisterActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityManager.getInstance().attach(this);
        setTitle("我是RegisterActivity");
    }

    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        ActivityManager.getInstance().detach(this);
    }

    public void click(View view) {
    
    
        ActivityManager.getInstance().finish(this);
        ActivityManager.getInstance().finish(LoginActivity.class);

    }
}

DebugActivity

class DebugActivity : AppCompatActivity() {
    
    
    private val TAG = javaClass.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ActivityManager.getInstance().attach(this)
        title = "我是DebugActivity"
    }

    fun click(view: View) {
    
    
        val intent = Intent(this, LoginActivity::class.java)
        startActivity(intent)
    }

    override fun onDestroy() {
    
    
        super.onDestroy()
        ActivityManager.getInstance().detach(this)
    }
}

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

Guess you like

Origin blog.csdn.net/weixin_46039528/article/details/132287718