Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีเคลียร์ Singleton Instance ใน Android

การล้าง/รีเซ็ต Singleton Instance ให้กลับสู่สถานะเริ่มต้น

บทความนี้สาธิตวิธี Clear Singleton Instance โดยการตั้งค่า Instance กลับเป็น null ทำให้ครั้งถัดไปที่เรียก getInstance() จะสร้าง Instance ใหม่ขึ้นมา (Lazy Initialization)

⚠️ คำเตือนสำคัญ

การตั้งค่า Instance = null มีความเสี่ยง:

  • Stale References: Object อื่นที่เก็บ Reference ไว้จะยังชี้ไป Instance เก่า
  • Race Condition: Thread อื่นอาจกำลังใช้ Instance อยู่
  • Memory Leak: Instance เก่าอาจไม่ถูก GC หากยังมี Reference

แนะนำ: ใช้ clearData() หรือ reset() method แทน ซึ่งปลอดภัยกว่าและคาดเดาได้

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → File → New Project → Empty Activity

ขั้นตอนที่ 2: Layout - activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="24dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Clear Singleton Instance Demo"
            android:textSize="20sp"
            android:textStyle="bold"
            android:layout_marginBottom="16dp" />

        <Button
            android:id="@+id/btnAddData"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="เพิ่มข้อมูลทดสอบ"
            android:layout_marginBottom="8dp" />

        <Button
            android:id="@+id/btnShowData"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="แสดงข้อมูล"
            android:layout_marginBottom="8dp" />

        <Button
            android:id="@+id/btnClearInstance"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Clear Instance (อันตราย)"
            android:backgroundTint="#F44336"
            android:layout_marginBottom="8dp" />

        <Button
            android:id="@+id/btnSafeClear"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Safe Clear (แนะนำ)"
            android:backgroundTint="#4CAF50" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

ขั้นตอนที่ 3: Singleton Class - ClearableSingleton.java

package com.example.clearsingtondemo;

import android.content.Context;
import org.json.JSONObject;

public class ClearableSingleton {

    private static volatile ClearableSingleton instance;
    private Context appContext;
    private JSONObject data;

    private ClearableSingleton() {
        data = new JSONObject();
    }

    // Double-Checked Locking
    public static ClearableSingleton getInstance() {
        if (instance == null) {
            synchronized (ClearableSingleton.class) {
                if (instance == null) {
                    instance = new ClearableSingleton();
                }
            }
        }
        return instance;
    }

    // ⚠️ วิธีอันตราย: ตั้งค่า Instance เป็น null
    public static void clearInstance() {
        instance = null;
    }

    // ✅ วิธีปลอดภัย: Clear ข้อมูลภายใน Instance เดิม
    public void clearData() {
        data = new JSONObject(); // สร้าง JSONObject ใหม่แทน
    }

    public void init(Context context) {
        if (appContext == null) {
            this.appContext = context.getApplicationContext();
        }
    }

    public void putData(String key, Object value) {
        try {
            data.put(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getDataString() {
        return data.toString();
    }

    public boolean isEmpty() {
        return data.length() == 0;
    }
}

ขั้นตอนที่ 4: MainActivity.java

package com.example.clearsingtondemo;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private ClearableSingleton singleton;
    private Button btnAddData, btnShowData, btnClearInstance, btnSafeClear;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        singleton = ClearableSingleton.getInstance();
        singleton.init(getApplicationContext());

        btnAddData = findViewById(R.id.btnAddData);
        btnShowData = findViewById(R.id.btnShowData);
        btnClearInstance = findViewById(R.id.btnClearInstance);
        btnSafeClear = findViewById(R.id.btnSafeClear);

        btnAddData.setOnClickListener(v -> {
            singleton.putData("key1", "Value 1");
            singleton.putData("key2", 123);
            singleton.putData("key3", true);
            Toast.makeText(this, "เพิ่มข้อมูลแล้ว", Toast.LENGTH_SHORT).show();
        });

        btnShowData.setOnClickListener(v -> {
            String data = singleton.getDataString();
            Toast.makeText(this, "ข้อมูล: " + data, Toast.LENGTH_LONG).show();
        });

        // ⚠️ วิธีอันตราย
        btnClearInstance.setOnClickListener(v -> {
            ClearableSingleton.clearInstance();
            Toast.makeText(this, "⚠️ Clear Instance แล้ว (Instance ถัดไปจะเป็นตัวใหม่)", Toast.LENGTH_LONG).show();
        });

        // ✅ วิธีปลอดภัย
        btnSafeClear.setOnClickListener(v -> {
            singleton.clearData();
            Toast.makeText(this, "✅ Clear Data แล้ว (Instance เดียวกัน ข้อมูลหาย)", Toast.LENGTH_SHORT).show();
        });
    }
}

การทดสอบความแตกต่าง

  1. กด "เพิ่มข้อมูลทดสอบ" → กด "แสดงข้อมูล" → เห็นข้อมูล
  2. ทดสอบ Safe Clear: กด "Safe Clear" → กด "แสดงข้อมูล" → ข้อมูลหาย แต่ Instance ยังคงเดิม
  3. ทดสอบ Dangerous Clear: กด "เพิ่มข้อมูล" → กด "Clear Instance" → กด "แสดงข้อมูล" → ข้อมูลหาย (Instance ใหม่)
  4. ลองเก็บ Reference ไว้: ClearableSingleton oldRef = ClearableSingleton.getInstance(); → กด Clear Instance → oldRef ยังชี้ Instance เก่า

เมื่อไหร่ควร Clear Instance จริงๆ?

  • Logout User: เคลียร์ User Session, Token, Cache
  • Switch Account: เปลี่ยนบัญชีผู้ใช้
  • App Reset: ฟีเจอร์ "Reset App Data" ใน Settings

Best Practice: ใช้ Interface แยก Clearable

public interface Clearable {
    void clear();
}

// Singleton implements Clearable
public class UserSession implements Clearable {
    private static volatile UserSession instance;
    private String token;
    private String userId;

    public static UserSession getInstance() { ... }

    public void setToken(String token) { this.token = token; }
    public String getToken() { return token; }

    @Override
    public void clear() {
        this.token = null;
        this.userId = null;
        // ไม่ตั้งค่า instance = null
    }
}

// ใช้งาน
public class AuthManager {
    public void logout() {
        UserSession.getInstance().clear(); // Safe
        // นำทางไป LoginActivity
    }
}

สรุป

การ Clear Singleton Instance (instance = null) ทำได้แต่ไม่แนะนำ เนื่องจากความเสี่ยง Stale Reference ควรใช้ clearData()/reset() method แทน ซึ่งปลอดภัย Thread-Safe และรักษา Instance Identity ไว้ได้