SharedPreferences คืออะไร?
SharedPreferences เป็นกลไกเก็บข้อมูล Key-Value แบบง่าย บน Internal Storage ของแอป ใช้สำหรับข้อมูลเล็กน้อย เช่น Settings, Preferences, Token, Flag, User Profile สำคัญคือ Private (แอปอื่นเข้าถึงไม่ได้)
Mode การเข้าถึง
| Mode | คำอธิบาย |
| MODE_PRIVATE (0) | เฉพาะแอปตัวนี้เข้าถึงได้ (Default, แนะนำ) |
| MODE_WORLD_READABLE | Deprecated (API 17+) - ไม่ปลอดภัย |
| MODE_WORLD_WRITEABLE | Deprecated (API 17+) - ไม่ปลอดภัย |
| MODE_MULTI_PROCESS | Deprecated (API 23+) - ไม่ทำงานเชื่อถือได้ |
API หลัก (SharedPreferences & Editor)
ดึง SharedPreferences Instance
// Default SharedPreferences (ต่อ Package Name)
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// Named SharedPreferences (แยกไฟล์)
SharedPreferences prefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
// ใน Activity
SharedPreferences prefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
SharedPreferences prefs = getPreferences(MODE_PRIVATE); // ใช้ชื่อ Activity
อ่านข้อมูล (SharedPreferences)
String name = prefs.getString("key_name", "Default Name");
int age = prefs.getInt("key_age", 0);
boolean isLoggedIn = prefs.getBoolean("key_logged_in", false);
float rating = prefs.getFloat("key_rating", 0f);
long timestamp = prefs.getLong("key_time", 0L);
Set stringSet = prefs.getStringSet("key_set", new HashSet<>());
// Check exists
boolean hasKey = prefs.contains("key_name");
// Get All (Map)
Map all = prefs.getAll();
เขียนข้อมูล (SharedPreferences.Editor)
SharedPreferences.Editor editor = prefs.edit();
editor.putString("key_name", "John Doe");
editor.putInt("key_age", 25);
editor.putBoolean("key_logged_in", true);
editor.putFloat("key_rating", 4.5f);
editor.putLong("key_time", System.currentTimeMillis());
editor.putStringSet("key_tags", new HashSet<>(Arrays.asList("android", "kotlin")));
// Remove
editor.remove("key_name");
editor.clear(); // ลบทั้งหมด
// Commit
editor.apply(); // Async - แนะนำสำหรับ UI Thread
editor.commit(); // Sync - คืนค่า boolean (success/fail) - ใช้ใน Background Thread
ตัวอย่างครบถ้วน: Login/Profile Screen
Layout (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
android:gravity="center">
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Not Logged In"
android:textSize="18sp"
android:layout_marginBottom="24dp" />
<EditText
android:id="@+id/etUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Username"
android:layout_marginBottom="12dp" />
<EditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login" />
<Button
android:id="@+id/btnLogout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Logout"
android:layout_marginTop="12dp" />
</LinearLayout>
MainActivity.java
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String PREFS_NAME = "auth_prefs";
private static final String KEY_USERNAME = "username";
private static final String KEY_IS_LOGGED_IN = "is_logged_in";
private static final String KEY_TOKEN = "auth_token";
private SharedPreferences prefs;
private EditText etUsername, etPassword;
private TextView tvStatus;
private Button btnLogin, btnLogout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
etUsername = findViewById(R.id.etUsername);
etPassword = findViewById(R.id.etPassword);
tvStatus = findViewById(R.id.tvStatus);
btnLogin = findViewById(R.id.btnLogin);
btnLogout = findViewById(R.id.btnLogout);
updateUI();
btnLogin.setOnClickListener(v -> attemptLogin());
btnLogout.setOnClickListener(v -> logout());
}
private void attemptLogin() {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "กรุณากรอก Username และ Password", Toast.LENGTH_SHORT).show();
return;
}
// จำลอง API Call (จริงๆ ควรใช้ Retrofit/Coroutine)
if ("admin".equals(username) && "1234".equals(password)) {
// Login Success - Save to SharedPreferences
String fakeToken = "token_" + System.currentTimeMillis();
prefs.edit()
.putString(KEY_USERNAME, username)
.putString(KEY_TOKEN, fakeToken)
.putBoolean(KEY_IS_LOGGED_IN, true)
.apply(); // Async
Toast.makeText(this, "Login สำเร็จ", Toast.LENGTH_SHORT).show();
updateUI();
} else {
Toast.makeText(this, "Username/Password ผิด", Toast.LENGTH_SHORT).show();
}
}
private void logout() {
prefs.edit()
.remove(KEY_USERNAME)
.remove(KEY_TOKEN)
.putBoolean(KEY_IS_LOGGED_IN, false)
.apply();
Toast.makeText(this, "Logout แล้ว", Toast.LENGTH_SHORT).show();
updateUI();
}
private void updateUI() {
boolean loggedIn = prefs.getBoolean(KEY_IS_LOGGED_IN, false);
if (loggedIn) {
String username = prefs.getString(KEY_USERNAME, "User");
tvStatus.setText("Logged in as: " + username);
etUsername.setVisibility(View.GONE);
etPassword.setVisibility(View.GONE);
btnLogin.setVisibility(View.GONE);
btnLogout.setVisibility(View.VISIBLE);
} else {
tvStatus.setText("Not Logged In");
etUsername.setVisibility(View.VISIBLE);
etPassword.setVisibility(View.VISIBLE);
btnLogin.setVisibility(View.VISIBLE);
btnLogout.setVisibility(View.GONE);
}
}
}
Best Practices และข้อควรระวัง
1. ใช้ Constants สำหรับ Keys
// ไม่ได้ Hardcode String ในโค้ด
public class PrefsKeys {
public static final String USER_TOKEN = "user_token";
public static final String USER_NAME = "user_name";
// ...
}
2. Encrypt ข้อมูลสำคัญ (Security)
// ใช้ EncryptedSharedPreferences (Jetpack Security)
implementation "androidx.security:security-crypto:1.1.0-alpha03"
MasterKey masterKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
SharedPreferences prefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
3. ใช้ apply() แทน commit() ใน UI Thread
apply(): เขียน Async ไม่ Block UI, ไม่สนใจ Result
commit(): เขียน Sync Block Thread, คืนค่า Boolean
4. Listener สำหรับการเปลี่ยนแปลง
prefs.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> {
if (KEY_IS_LOGGED_IN.equals(key)) {
boolean loggedIn = sharedPreferences.getBoolean(key, false);
// Update UI หรือ Notify อื่นๆ
}
});
// Unregister ใน onDestroy
@Override
protected void onDestroy() {
super.onDestroy();
prefs.unregisterOnSharedPreferenceChangeListener(listener);
}
5. Backup/Restore (Auto Backup)
SharedPreferences vs DataStore vs Room
| Feature | SharedPreferences | Preferences DataStore | Room Database |
| Data Type | Primitive + StringSet | Primitive + StringSet | Any Object (Entity) |
| Async API | ❌ (apply async แต่ read sync) | ✅ Flow/RxJava | ✅ Flow/RxJava/Suspend |
| Type Safety | ❌ | ❌ | ✅ Compile-time |
| Query/Index | ❌ | ❌ | ✅ SQL Query |
| Migration | Manual | Auto (Simple) | Auto (Complex) |
| Size Limit | ~1-2 MB | ~1-2 MB | Large (Disk) |
| Use Case | Simple Settings, Flags | Modern Settings, Proto | App Data, Cache, Offline |
สรุป
- SharedPreferences เหมาะสำหรับ ข้อมูลเล็ก, Simple, Key-Value
- ใช้
MODE_PRIVATE เสมอ
- Key ใช้ Constants, Value ใช้
apply()
- ข้อมูล Sensitve ใช้ EncryptedSharedPreferences
- โปรเจกต์ใหม่/Complex Data → ใช้ DataStore (Proto) หรือ Room