SharedPreferences และ ArrayList
SharedPreferences เก็บข้อมูล Key-Value แบบ Primitive (String, Int, Boolean, Float, Long, StringSet) ไม่รองรับ ArrayList/List โดยตรง วิธีแก้คือ แปลง ArrayList → JSON String → เก็บใน SharedPreferences
วิธีที่ 1: ใช้ Gson (แนะนำ - ทรงพลังที่สุด)
Dependency
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
}
Helper Class: SharedPrefsManager.java
import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class SharedPrefsManager {
private static final String PREFS_NAME = "app_prefs";
private static final String KEY_USER_LIST = "user_list";
private final SharedPreferences prefs;
private final Gson gson;
private final Type listType = new TypeToken>() {}.getType();
public SharedPrefsManager(Context context) {
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
gson = new Gson();
}
// Save List
public void saveUserList(List userList) {
String json = gson.toJson(userList);
prefs.edit().putString(KEY_USER_LIST, json).apply();
}
// Load List
public List getUserList() {
String json = prefs.getString(KEY_USER_LIST, null);
if (json == null) return new ArrayList<>();
return gson.fromJson(json, listType);
}
// Add Single Item
public void addUser(User user) {
List list = getUserList();
list.add(user);
saveUserList(list);
}
// Clear
public void clearUserList() {
prefs.edit().remove(KEY_USER_LIST).apply();
}
}
Model Class: User.java
public class User {
private String name;
private String email;
private int age;
// Constructors, Getters, Setters
public User(String name, String email, int age) {
this.name = name;
this.email = email;
this.age = age;
}
// ... getters/setters
}
ใช้งานใน Activity
public class MainActivity extends AppCompatActivity {
private SharedPrefsManager prefsManager;
private EditText etName, etEmail, etAge;
private TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prefsManager = new SharedPrefsManager(this);
etName = findViewById(R.id.etName);
etEmail = findViewById(R.id.etEmail);
etAge = findViewById(R.id.etAge);
tvResult = findViewById(R.id.tvResult);
findViewById(R.id.btnSave).setOnClickListener(v -> saveUser());
findViewById(R.id.btnLoad).setOnClickListener(v -> loadUsers());
}
private void saveUser() {
String name = etName.getText().toString().trim();
String email = etEmail.getText().toString().trim();
String ageStr = etAge.getText().toString().trim();
if (name.isEmpty() || email.isEmpty() || ageStr.isEmpty()) {
Toast.makeText(this, "กรอกข้อมูลให้ครบ", Toast.LENGTH_SHORT).show();
return;
}
User user = new User(name, email, Integer.parseInt(ageStr));
prefsManager.addUser(user);
Toast.makeText(this, "บันทึกแล้ว", Toast.LENGTH_SHORT).show();
clearInputs();
}
private void loadUsers() {
List users = prefsManager.getUserList();
if (users.isEmpty()) {
tvResult.setText("ยังไม่มีข้อมูล");
return;
}
StringBuilder sb = new StringBuilder();
for (User u : users) {
sb.append(u.getName()).append(" - ").append(u.getEmail()).append(" (").append(u.getAge()).append(")").append("\n");
}
tvResult.setText(sb.toString());
}
}
วิธีที่ 2: ใช้ StringSet (สำหรับ String List เท่านั้น)
SharedPreferences รองรับ Set<String> ตั้งแต่ API 11 (Honeycomb)
// Save Set> ⚠️ **ข้อจำกัด:** ไม่รองรับ Object, ไม่รองรับ Duplicate, **ไม่รับประกัน Order** (HashSet)stringSet = new HashSet<>(stringList); prefs.edit().putStringSet("string_list_key", stringSet).apply(); // Load Set savedSet = prefs.getStringSet("string_list_key", new HashSet<>()); List list = new ArrayList<>(savedSet); // Order ไม่รับประกัน!
วิธีที่ 3: Manual JSON (ไม่ใช้ Library)
// แปลง ArrayList> ❌ **ไม่แนะนำ** ใช้แค่กรณีห้ามเพิ่ม Dependency (Gson เล็กมาก ~200KB คุ้มค่า)เป็น JSON Array String แบบ Manual public String listToJson(List list) { StringBuilder sb = new StringBuilder("["); for (int i = 0; i < list.size(); i++) { sb.append('"').append(list.get(i).replace("\"", "\\\"")).append('"'); if (i < list.size() - 1) sb.append(","); } sb.append("]"); return sb.toString(); } // Parse กลับ (Simple - ไม่รองรับ Escape ทุกกรณี) public List jsonToList(String json) { List list = new ArrayList<>(); if (json == null || json.length() < 2) return list; String content = json.substring(1, json.length() - 1); // remove [] String[] items = content.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); // Split by comma not in quotes for (String item : items) { String trimmed = item.trim(); if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) { trimmed = trimmed.substring(1, trimmed.length() - 1); } list.add(trimmed.replace("\\\"", "\"")); } return list; }
ข้อควรระวังและ Best Practices
1. ขนาดข้อมูล (Size Limit)
- SharedPreferences เก็บใน XML File บน Internal Storage
- ไม่ควรเกิน 1-2 MB (อ่าน/เขียนช้า, ANR Risk)
- List ใหญ่/Object ใหญ่ → ใช้ Room Database หรือ DataStore
2. Thread Safety
apply()= Async (Non-blocking) - ใช้ใน UI Thread ได้commit()= Sync (Blocking) - ไม่ควรใช้ใน UI Thread- Gson toJson/fromJson ใช้เวลา → ให้รันบน Background Thread (Coroutines/RxJava)
3. TypeToken สำหรับ Generic
// ถูกต้อง Type listType = new TypeToken>() {}.getType(); // ผิด (Type Erasure) Type listType = new TypeToken >() {}.getType(); // จะเป็น List
4. Migration ไป DataStore (Modern)
Jetpack DataStore (Preferences/DataStore) แทน SharedPreferences:
// build.gradle implementation "androidx.datastore:datastore-preferences:1.0.0" // หรือ Proto DataStore สำหรับ Object/Complex Type implementation "androidx.datastore:datastore:1.0.0" // Proto DataStore ใช้ Protocol Buffers รองรับ List/Object ได้โดยตรง
สรุป
| วิธี | เหมาะสำหรับ | ข้อดี | ข้อเสีย |
|---|---|---|---|
| Gson + JSON String | Object List, Complex Data | Type Safety, Flexible, Standard | ต้องเพิ่ม Library, Serialize/Deserialize Cost |
| StringSet | String List เท่านั้น | Built-in, ไม่ต้อง Library | No Order, No Duplicate, String Only |
| Manual JSON | ห้ามเพิ่ม Dependency | Zero Dependency | Error-prone, Maintenance Hell |
| Room/DataStore | Production App, Large Data | Robust, Query, Observe, Type-safe | Setup ซับซ้อนกว่า |
แนะนำ: ใช้ Gson + SharedPreferences สำหรับข้อมูลเล็ก/Simple List, ย้ายไป Room/Proto DataStore เมื่อข้อมูลใหญ่หรือซับซ้อน