การล้าง/รีเซ็ต 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();
});
}
}
การทดสอบความแตกต่าง
- กด "เพิ่มข้อมูลทดสอบ" → กด "แสดงข้อมูล" → เห็นข้อมูล
- ทดสอบ Safe Clear: กด "Safe Clear" → กด "แสดงข้อมูล" → ข้อมูลหาย แต่ Instance ยังคงเดิม
- ทดสอบ Dangerous Clear: กด "เพิ่มข้อมูล" → กด "Clear Instance" → กด "แสดงข้อมูล" → ข้อมูลหาย (Instance ใหม่)
- ลองเก็บ 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 ไว้ได้