Skip to Content

SharedPreferences trong Android

1. SharedPreferences là gì?

Khi bạn xây dựng ứng dụng, thường có nhu cầu lưu trữ một số thông tin đơn giản như:

  • User đã đăng nhập chưa?
  • Theme sáng hay tối?
  • Ngôn ngữ ưa thích?
  • Lần cuối user mở app là khi nào?

SharedPreferences giải quyết nhu cầu này bằng cách lưu trữ dữ liệu dạng key-value (chìa khóa - giá trị) vào một file XML trong bộ nhớ của app.

Đặc điểm quan trọng:

  • Persistent: Dữ liệu vẫn còn sau khi tắt app
  • Đơn giản: Chỉ lưu được các kiểu dữ liệu cơ bản
  • Private: Chỉ app của bạn có thể đọc được

Lưu ý: Google khuyến nghị dùng DataStore thay cho SharedPreferences trong projects mới vì nó an toàn hơn với async operations.


2. Lấy SharedPreferences

Trước khi đọc/ghi dữ liệu, bạn cần lấy instance của SharedPreferences:

// Cách 1: Tạo file preferences riêng với tên tùy chọn val prefs = getSharedPreferences("my_app_settings", Context.MODE_PRIVATE)

Giải thích:

  • "my_app_settings": Tên file để lưu preferences (bạn đặt tùy ý)
  • Context.MODE_PRIVATE: Chỉ app này có thể đọc file này
// Cách 2: Dùng default preferences của app val defaultPrefs = PreferenceManager.getDefaultSharedPreferences(context)

3. Lưu dữ liệu

SharedPreferences hỗ trợ các kiểu dữ liệu sau:

  • String - Chuỗi văn bản
  • Int - Số nguyên
  • Long - Số nguyên lớn
  • Float - Số thập phân
  • Boolean - True/False
  • Set<String> - Tập hợp các chuỗi

Ví dụ lưu thông tin user:

val prefs = getSharedPreferences("user_data", Context.MODE_PRIVATE) // Cách viết ngắn gọn với Kotlin extension prefs.edit { putString("username", "alice") // Lưu tên user putInt("age", 25) // Lưu tuổi putBoolean("is_logged_in", true) // Lưu trạng thái đăng nhập putFloat("score", 98.5f) // Lưu điểm số putLong("last_login", System.currentTimeMillis()) // Lưu thời gian putStringSet("favorite_topics", setOf("kotlin", "android", "compose")) }

Giải thích từng dòng:

  1. prefs.edit { } - Mở “phiên chỉnh sửa” preferences
  2. putString("username", "alice") - Lưu giá trị “alice” với key là “username”
  3. Khi block code kết thúc, dữ liệu tự động được save

4. Đọc dữ liệu

Khi đọc, bạn cần cung cấp giá trị mặc định phòng trường hợp key chưa tồn tại:

val prefs = getSharedPreferences("user_data", Context.MODE_PRIVATE) // Đọc tên user, nếu chưa có thì trả về chuỗi rỗng "" val username = prefs.getString("username", "") ?: "" // Đọc tuổi, nếu chưa có thì trả về 0 val age = prefs.getInt("age", 0) // Đọc trạng thái đăng nhập, mặc định là false (chưa đăng nhập) val isLoggedIn = prefs.getBoolean("is_logged_in", false) // Đọc điểm, mặc định là 0 val score = prefs.getFloat("score", 0f) // Đọc thời gian đăng nhập cuối val lastLogin = prefs.getLong("last_login", 0L) // Đọc danh sách topics yêu thích val topics = prefs.getStringSet("favorite_topics", emptySet()) ?: emptySet()

Tại sao cần giá trị mặc định?

  • Lần đầu mở app, chưa có dữ liệu nào được lưu
  • Nếu không có giá trị mặc định, app sẽ crash

5. Xóa dữ liệu

prefs.edit { remove("username") // Xóa một key cụ thể } prefs.edit { clear() // Xóa TẤT CẢ dữ liệu trong file preferences này }

Khi nào cần xóa?

  • User đăng xuất → xóa thông tin đăng nhập
  • Reset settings → xóa tất cả preferences

6. apply() vs commit() - Cách lưu dữ liệu

Có 2 cách để lưu thay đổi:

apply() - Lưu bất đồng bộ (Khuyến nghị)

prefs.edit { putString("key", "value") } // apply() được gọi tự động
  • Không block UI thread
  • Nhanh hơn
  • Không biết kết quả (thành công hay thất bại)

commit() - Lưu đồng bộ

val success = prefs.edit { putString("key", "value") }.commit() if (success) { Log.d("Prefs", "Lưu thành công!") } else { Log.e("Prefs", "Lưu thất bại!") }
  • Block thread cho đến khi lưu xong
  • Trả về true nếu thành công
  • Dùng khi cần chắc chắn dữ liệu đã được lưu

Best Practice: Hầu hết các trường hợp, dùng apply() là đủ.


7. Wrapper Class - Cách tổ chức code tốt hơn

Thay vì gọi prefs.getString(...) ở nhiều nơi, hãy tạo một class wrapper:

class UserPreferences(context: Context) { // Tạo preferences một lần private val prefs = context.getSharedPreferences( "user_prefs", Context.MODE_PRIVATE ) // Property cho username - đọc/ghi dễ dàng var username: String get() = prefs.getString("username", "") ?: "" set(value) = prefs.edit { putString("username", value) } // Property cho trạng thái đăng nhập var isLoggedIn: Boolean get() = prefs.getBoolean("is_logged_in", false) set(value) = prefs.edit { putBoolean("is_logged_in", value) } // Property cho dark mode var isDarkMode: Boolean get() = prefs.getBoolean("dark_mode", false) set(value) = prefs.edit { putBoolean("dark_mode", value) } // Function để xóa tất cả khi logout fun clearAll() = prefs.edit { clear() } }

Sử dụng wrapper class:

// Ở đâu đó trong app val userPrefs = UserPreferences(context) // Đọc if (userPrefs.isLoggedIn) { showWelcome("Xin chào ${userPrefs.username}!") } // Ghi userPrefs.username = "Alice" userPrefs.isLoggedIn = true // Logout userPrefs.clearAll()

Lợi ích của wrapper class:

  • Code gọn gàng hơn
  • Key names được tập trung một chỗ
  • Intellisense hỗ trợ khi code
  • Dễ test

8. Sử dụng với Jetpack Compose

@Composable fun SettingsScreen() { val context = LocalContext.current // Tạo preferences instance (chỉ tạo một lần với remember) val prefs = remember { context.getSharedPreferences("settings", Context.MODE_PRIVATE) } // State cho dark mode, khởi tạo từ preferences var darkMode by remember { mutableStateOf(prefs.getBoolean("dark_mode", false)) } // UI Row( modifier = Modifier.fillMaxWidth().padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween ) { Text("Chế độ tối") Switch( checked = darkMode, onCheckedChange = { isChecked -> // Cập nhật state darkMode = isChecked // Lưu vào preferences prefs.edit { putBoolean("dark_mode", isChecked) } } ) } }

Giải thích flow:

  1. Khi mở màn hình → đọc giá trị từ prefs → hiển thị Switch
  2. User toggle Switch → cập nhật state → lưu vào prefs
  3. Lần sau mở app → đọc lại từ prefs → giữ nguyên trạng thái

9. Encrypted SharedPreferences - Lưu dữ liệu nhạy cảm

Với dữ liệu nhạy cảm như token, password… cần mã hóa:

// Thêm dependency // implementation "androidx.security:security-crypto:1.1.0-alpha06" // Tạo master key để mã hóa val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() // Tạo encrypted preferences val securePrefs = EncryptedSharedPreferences.create( context, "secure_prefs", // Tên file masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) // Sử dụng giống hệt SharedPreferences thông thường securePrefs.edit { putString("auth_token", "eyJhbGciOiJIUzI1NiIs...") } val token = securePrefs.getString("auth_token", null)

10. Khi nào dùng SharedPreferences?

Use CasePhù hợpGiải thích
Settings (theme, language)✅ CóDữ liệu đơn giản, ít thay đổi
User preferences✅ CóOn/off features
Auth tokens✅ CóDùng EncryptedSharedPreferences
Trạng thái nhỏ✅ CóisFirstLaunch, lastSyncTime
Danh sách items❌ KhôngDùng Room Database
JSON objects❌ KhôngDùng DataStore hoặc Room
Data thay đổi thường xuyên❌ KhôngDùng DataStore (có Flow support)

📝 Tóm tắt cho người mới

  1. SharedPreferences lưu dữ liệu dạng key-value đơn giản
  2. Lưu: prefs.edit { putString("key", "value") }
  3. Đọc: prefs.getString("key", defaultValue)
  4. Luôn cung cấp giá trị mặc định khi đọc
  5. Dùng apply() (khuyến nghị) hoặc commit()
  6. Tạo Wrapper class để code gọn gàng hơn
  7. Dùng EncryptedSharedPreferences cho dữ liệu nhạy cảm
  8. Cân nhắc dùng DataStore cho projects mới
Last updated on