SharedPreferences là một kho lưu trữ dữ liệu khóa-giá trị trên Android được thiết kế để lưu các cài đặt đơn giản và cấu hình ứng dụng. Dữ liệu được lưu trữ trong tệp XML trên thiết bị và chỉ có thể truy cập được trong ứng dụng đã tạo ra nó. Theo tài liệu chính thức Android Developers, 2025, SharedPreferences hỗ trợ lưu trữ các kiểu nguyên thủy: String, Int, Boolean, Float, Long và Set<String>. Đây là giải pháp đơn giản và nhanh nhất để lưu một lượng nhỏ cài đặt người dùng mà không cần truy vấn SQL hoặc làm việc trực tiếp với hệ thống tệp.
Những Điểm Chính
SharedPreferences là một cơ chế tích hợp của Android để lưu trữ các cặp khóa-giá trị trong tệp XML trên bộ nhớ trong của thiết bị. Nó có sẵn từ API Level 1 và không yêu cầu thư viện bổ sung. Mục đích chính là lưu tùy chọn người dùng, trạng thái giao diện, cờ lần khởi chạy đầu tiên và các dữ liệu đơn giản khác không yêu cầu cơ sở dữ liệu có cấu trúc.
Mỗi tệp SharedPreferences được liên kết với một tên cụ thể và chế độ truy cập. Theo mặc định, chế độ Context.MODE_PRIVATE được sử dụng, giới hạn quyền truy cập tệp chỉ trong ứng dụng hiện tại. Trước đây Android hỗ trợ các chế độ MODE_WORLD_READABLE và MODE_WORLD_WRITEABLE, nhưng chúng đã bị không dùng nữa từ API Level 17 và bị xóa hoàn toàn trong Android 7.0 (API 24) vì lý do bảo mật.
Mặc dù đơn giản, SharedPreferences được sử dụng trong hàng triệu ứng dụng Android. Theo Google, hơn 90% ứng dụng được xuất bản trên Google Play sử dụng SharedPreferences để lưu trữ cài đặt. Tuy nhiên, đối với các tình huống phức tạp (khối lượng dữ liệu lớn, an toàn kiểu, bất đồng bộ), Google khuyến nghị các giải pháp hiện đại hơn như Preferences DataStore từ thư viện Android Jetpack.
Về mặt vật lý, SharedPreferences được lưu trữ dưới dạng tệp XML trong thư mục ứng dụng: /data/data/{package_name}/shared_prefs/{file_name}.xml. Tệp chứa phần tử gốc <map> với các phần tử con <string>, <int>, <boolean>, <float> và <long> tùy theo loại giá trị được lưu trữ. Kích thước tệp không bị giới hạn, nhưng đối với khối lượng dữ liệu lớn (hơn 100 KB), hiệu suất đọc và ghi bắt đầu giảm đáng kể.
Các tệp SharedPreferences không được mã hóa theo mặc định. Dữ liệu được lưu trữ dưới dạng văn bản thuần túy trên hệ thống tệp của thiết bị. Để lưu trữ dữ liệu nhạy cảm (token, mật khẩu), nên sử dụng EncryptedSharedPreferences từ thư viện AndroidX Security, tự động mã hóa khóa và giá trị bằng AES256-GCM.
SharedPreferences hoạt động theo nguyên tắc bộ nhớ đệm trong RAM với đồng bộ hóa đĩa định kỳ. Khi truy cập tệp lần đầu tiên (qua getSharedPreferences), Android tải tệp XML vào RAM và phân tích nó thành đối tượng Map. Tất cả các thao tác đọc tiếp theo được thực hiện từ bộ nhớ mà không cần đọc lại từ đĩa. Điều này đảm bảo tốc độ truy cập dữ liệu cao.
Các thao tác ghi sử dụng Editor — một bộ đệm thay đổi nội bộ. Khi nhà phát triển gọi putString hoặc putBoolean, các thay đổi được lưu trữ trong đối tượng Editor trong bộ nhớ. Việc ghi thực tế lên đĩa xảy ra khi phương thức commit (đồng bộ) hoặc apply (không đồng bộ) được gọi. Cho đến khi các phương thức này được gọi, dữ liệu không được lưu và nếu ứng dụng gặp sự cố bất ngờ, các thay đổi có thể bị mất.
Để lấy một phiên bản SharedPreferences, hai phương thức được sử dụng: getPreferences và getSharedPreferences. Phương thức đầu tiên chỉ khả dụng trong Activity và tạo tệp có tên Activity. Phương thức thứ hai linh hoạt hơn, chấp nhận tên tệp và chế độ truy cập, và có thể truy cập từ bất kỳ ngữ cảnh nào (Application, Activity, Service). Nên sử dụng getSharedPreferences với tên tệp tương ứng với mô-đun hoặc chức năng của ứng dụng.
// Lấy SharedPreferences
val prefs = context.getSharedPreferences(
"user_settings", Context.MODE_PRIVATE
)
// Ghi dữ liệu
with(prefs.edit()) {
putString("username", "Anna")
putInt("age", 28)
putBoolean("isLoggedIn", true)
apply()
}
// Đọc dữ liệu
val username = prefs.getString("username", "")
val age = prefs.getInt("age", 0)
val isLoggedIn = prefs.getBoolean("isLoggedIn", false)
Khi sử dụng MODE_MULTI_PROCESS (không dùng nữa), SharedPreferences đồng bộ hóa giữa các tiến trình. Tuy nhiên, sự đồng bộ hóa này không đảm bảo tính nguyên tử và Google khuyến nghị tránh sử dụng SharedPreferences trong các tình huống đa tiến trình. Trong những trường hợp như vậy, tốt hơn nên sử dụng ContentProvider, Room với truy cập liên tiến trình hoặc DataStore.
SharedPreferences cung cấp một tập hợp các phương thức để đọc dữ liệu theo khóa và giao diện Editor để ghi. Mỗi phương thức đọc chấp nhận hai tham số: một khóa và một giá trị mặc định được trả về nếu không tìm thấy khóa. Giá trị mặc định cũng xác định kiểu trả về: getString trả về String, getInt trả về Int, v.v.
| Phương thức đọc | Phương thức ghi | Kiểu dữ liệu |
|---|---|---|
| getString | putString | String |
| getInt | putInt | Int |
| getBoolean | putBoolean | Boolean |
| getFloat | putFloat | Float |
| getLong | putLong | Long |
| getStringSet | putStringSet | Set<String> |
Editor là một đối tượng nội bộ của SharedPreferences thu thập các thay đổi trong bộ đệm. Sau khi thực hiện tất cả các thay đổi, nhà phát triển gọi commit() (ghi đồng bộ) hoặc apply() (ghi không đồng bộ). Sự khác biệt rất quan trọng: commit chặn luồng hiện tại cho đến khi ghi hoàn tất lên đĩa và trả về boolean (thành công/thất bại), trong khi apply thực hiện ghi trong luồng nền và trả lại quyền điều khiển ngay lập tức nhưng không trả về kết quả.
Nên sử dụng apply thay vì commit trong mọi trường hợp không cần biết kết quả ghi. apply nhanh hơn và không chặn luồng giao diện người dùng. commit chỉ nên được sử dụng khi cần biết liệu dữ liệu đã được lưu thành công hay chưa, hoặc khi làm việc với chế độ đa tiến trình. Để xóa các khóa riêng lẻ, phương thức remove được sử dụng, để xóa hoàn toàn — clear. Tất cả các thao tác xóa cũng được thực hiện qua Editor.
// Nhiều thay đổi - một apply
prefs.edit {
putString("theme", "dark")
putBoolean("notifications", false)
remove("old_key")
}
// Trình lắng nghe thay đổi giá trị
prefs.registerOnSharedPreferenceChangeListener { prefs, key ->
Log.d("TAG", "Khóa đã thay đổi: $key")
}
Từ Android 12 (API 31), SharedPreferences đã được cải tiến với hỗ trợ registerOnSharedPreferenceChangeListener với tính năng hủy đăng ký tự động qua Lifecycle. Điều này giúp tránh rò rỉ bộ nhớ liên quan đến các trình lắng nghe bị quên. Trong các phiên bản cũ hơn, nhà phát triển phải tự gọi unregisterOnSharedPreferenceChangeListener trong onDestroy hoặc onStop của thành phần.
Mặc dù được sử dụng rộng rãi, SharedPreferences không phải là giải pháp phổ quát cho mọi tình huống lưu trữ dữ liệu trên Android. Tùy thuộc vào khối lượng dữ liệu, yêu cầu an toàn kiểu và hiệu suất, Google khuyến nghị nhiều lựa chọn thay thế khác nhau có trong Android Jetpack và thư viện Android tiêu chuẩn.
| Giải pháp | Khi nào sử dụng | Nhược điểm |
|---|---|---|
| SharedPreferences | Cài đặt nhỏ (tối đa 100 khóa) | Không an toàn kiểu, đọc đồng bộ |
| DataStore | Cài đặt độ phức tạp trung bình với coroutine | Không tương thích ngược dưới API 14 |
| Room | Dữ liệu có cấu trúc và danh sách | Quá mức cho 3-5 cài đặt |
| EncryptedSharedPreferences | Dữ liệu nhạy cảm và token | Phụ thuộc vào AndroidX Security |
DataStore là một thư viện Android Jetpack được Google giới thiệu như một sự thay thế cho SharedPreferences. Nó cung cấp hai biến thể: Preferences DataStore (khóa-giá trị, như SharedPreferences) và Proto DataStore (lưu trữ có kiểu qua Protocol Buffers). DataStore sử dụng coroutine và Flow cho hoạt động không đồng bộ, đảm bảo an toàn kiểu và tự động xử lý di chuyển phiên bản. Google khuyến nghị DataStore cho tất cả các dự án mới.
Ưu điểm chính của DataStore là tính bất đồng bộ ở cấp độ API. Tất cả các thao tác đọc trả về Flow và các thao tác ghi là các hàm suspend. Điều này loại bỏ hoàn toàn việc chặn luồng giao diện người dùng có thể xảy ra khi đọc đồng bộ SharedPreferences. Ngoài ra, DataStore đảm bảo tính nhất quán dữ liệu: việc ghi được thực hiện trong một giao dịch và khi thất bại, tất cả các thay đổi được hoàn tác.
Hãy xem xét một ví dụ thực tế: cài đặt chủ đề (sáng/tối/hệ thống) trong ứng dụng Android. Người dùng chọn một chủ đề và lựa chọn được lưu trong SharedPreferences. Khi khởi chạy ứng dụng sau đó, chủ đề được khôi phục từ cài đặt đã lưu. Để cập nhật giao diện phản ứng, việc quan sát thay đổi qua SharedPreferences.OnSharedPreferenceChangeListener được sử dụng.
Hãy tạo một lớp ThemePreferences đóng gói tất cả công việc với SharedPreferences cho chủ đề. Lớp này cung cấp các phương thức getTheme (đọc), setTheme (ghi) và observeTheme (quan sát). Tên tệp cài đặt sẽ là “app_preferences” với chế độ MODE_PRIVATE. Để thuận tiện, các khóa được đặt trong đối tượng companion dưới dạng hằng số.
class ThemePreferences(context: Context) {
companion object {
private const val PREF_NAME = "app_preferences"
private const val KEY_THEME = "theme_mode"
const val THEME_LIGHT = "light"
const val THEME_DARK = "dark"
const val THEME_SYSTEM = "system"
}
private val prefs = context
.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
fun getTheme(): String =
prefs.getString(KEY_THEME, THEME_SYSTEM) ?: THEME_SYSTEM
fun setTheme(theme: String) {
prefs.edit { putString(KEY_THEME, theme) }
}
fun observeTheme(callback: (String) -> Unit) {
prefs.registerOnSharedPreferenceChangeListener { _, key ->
if (key == KEY_THEME) {
callback.invoke(getTheme())
}
}
}
}
Trong Activity hoặc Fragment, việc lấy phiên bản ThemePreferences được thực hiện qua ngữ cảnh ứng dụng. Khi khởi tạo, getTheme được gọi để đặt chủ đề hiện tại. Khi người dùng chọn chủ đề mới, setTheme được gọi và qua observeTheme, giao diện được cập nhật mà không cần khởi động lại Activity. Điều quan trọng là hủy đăng ký trình lắng nghe trong onDestroy để tránh rò rỉ bộ nhớ, đặc biệt nếu Activity được tạo lại khi thay đổi cấu hình.
Đối với ứng dụng có phiên bản mục tiêu tối thiểu Android 12+, nên sử dụng registerOnSharedPreferenceChangeListener cùng với LifecycleObserver. Điều này tự động quản lý việc đăng ký và hủy đăng ký khi thay đổi vòng đời thành phần. Đối với các phiên bản cũ hơn, việc đăng ký và hủy đăng ký phải được quản lý thủ công, đây là nguồn lỗi thường gặp trong các ứng dụng sản xuất sử dụng SharedPreferences.
Câu Hỏi Thường Gặp
SharedPreferences trực tiếp chỉ hỗ trợ các kiểu nguyên thủy và Set<String>. Để lưu trữ đối tượng, cần tuần tự hóa chúng thành chuỗi JSON qua Gson hoặc Moshi, lưu qua putString và giải tuần tự hóa khi đọc. Đối với các đối tượng phức tạp có nhiều trường, nên sử dụng Room thay vì SharedPreferences với tuần tự hóa JSON.
Có, SharedPreferences an toàn cho luồng. Tất cả các thao tác đọc và ghi được đồng bộ hóa ở cấp độ đối tượng SharedPreferences và Editor của nó. Tuy nhiên, khi sử dụng chế độ đa tiến trình, việc đồng bộ hóa không được đảm bảo. Đối với truy cập đồng thời từ nhiều luồng trong cùng một ứng dụng, SharedPreferences an toàn mà không cần khóa bổ sung.
Để xóa hoàn toàn tất cả dữ liệu khỏi SharedPreferences, hãy gọi phương thức clear() trên Editor và áp dụng các thay đổi qua apply. Nếu cần xóa chính tệp XML, hãy sử dụng deleteSharedPreferences(name) trên ngữ cảnh. Xóa dữ liệu ứng dụng qua Cài đặt → Ứng dụng → Xóa dữ liệu cũng xóa tất cả các tệp SharedPreferences.
Đối với các dự án mới, Google khuyến nghị DataStore như một sự thay thế cho SharedPreferences. DataStore cung cấp hoạt động không đồng bộ với coroutine, an toàn kiểu (Proto DataStore) và di chuyển tự động. SharedPreferences chỉ nên được chọn cho các dự án có phiên bản tối thiểu dưới API 14 hoặc khi cần tích hợp nhanh mà không có phụ thuộc bổ sung.
Để mã hóa dữ liệu, hãy sử dụng EncryptedSharedPreferences từ thư viện AndroidX Security. Nó tự động mã hóa khóa và giá trị bằng AES-256 GCM. Quy trình thiết lập rất đơn giản: getSharedPreferences được thay thế bằng EncryptedSharedPreferences.create với việc chỉ định khóa chính từ Android Keystore.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm