SharedPreferences е ключ-стойност хранилище за данни в Android, предназначено за съхранение на прости настройки и конфигурации на приложението. Данните се съхраняват в XML файл на устройството и са достъпни само в рамките на приложението, което ги е създало. Според официалната документация Android Developers, 2025, SharedPreferences поддържа съхранение на примитивни типове: String, Int, Boolean, Float, Long и Set<String>. Това е най-простото и бързо решение за запазване на малки количества потребителски настройки без необходимост от SQL заявки или директна работа с файловата система.
Основни неща
SharedPreferences е вграден механизъм на Android за съхранение на двойки ключ-стойност в XML файл във вътрешната памет на устройството. Достъпен е от API Level 1 и не изисква свързване на допълнителни библиотеки. Основно предназначение — съхранение на потребителски настройки, състояние на интерфейса, флагове за първо стартиране и други прости данни, които не изискват структурирана база данни.
Всеки SharedPreferences файл е свързан с конкретно име и режим на достъп. По подразбиране се използва режим Context.MODE_PRIVATE, който ограничава достъпа до файла само до текущото приложение. Преди Android поддържаше режими MODE_WORLD_READABLE и MODE_WORLD_WRITEABLE, но те бяха обявени за остарели от API Level 17 и напълно премахнати в Android 7.0 (API 24) от съображения за сигурност.
Въпреки своята простота, SharedPreferences се използва в милиони Android приложения. Според Google, над 90% от приложенията, публикувани в Google Play, използват SharedPreferences за съхранение на настройки. Въпреки това, за сложни сценарии (големи обеми данни, типова безопасност, асинхронност) Google препоръчва по-модерни решения, като Preferences DataStore от библиотеката Android Jetpack.
Физически, SharedPreferences се съхранява като XML файл в директорията на приложението: /data/data/{package_name}/shared_prefs/{file_name}.xml. Файлът съдържа основен елемент <map> с дъщерни елементи <string>, <int>, <boolean>, <float> и <long> в зависимост от типа на запазената стойност. Размерът на файла не е ограничен, но за големи обеми данни (над 100 KB) производителността на четене и запис започва забележимо да намалява.
Файловете на SharedPreferences не са криптирани по подразбиране. Данните се съхраняват в отворен вид във файловата система на устройството. За съхранение на чувствителни данни (токени, пароли) се препоръчва използването на EncryptedSharedPreferences от библиотеката AndroidX Security, която автоматично криптира ключовете и стойностите с AES256-GCM.
SharedPreferences работи на принципа на кеширане в паметта с периодична синхронизация на диска. При първия достъп до файла (чрез getSharedPreferences) Android зарежда XML файла в RAM и го парсира в обект Map. Всички последващи операции за четене се изпълняват от паметта, без повторно четене от диска. Това осигурява висока скорост на достъп до данните.
Операциите за запис използват Editor — вътрешен буфер за промени. Когато разработчикът извика putString или putBoolean, промените се съхраняват в обекта Editor в паметта. Реалният запис на диска се случва при извикване на метода commit (синхронно) или apply (асинхронно). До извикването на тези методи данните не се запазват и при внезапно прекратяване на приложението промените могат да бъдат загубени.
За получаване на инстанция на SharedPreferences се използват два метода: getPreferences и getSharedPreferences. Първият е достъпен само в рамките на Activity и създава файл с името на Activity. Вторият е по-гъвкав, приема име на файл и режим на достъп, и е достъпен от всеки контекст (Application, Activity, Service). Препоръчва се използването на getSharedPreferences с име на файл, съответстващо на модула или функционалността на приложението.
// Получаване на SharedPreferences
val prefs = context.getSharedPreferences(
"user_settings", Context.MODE_PRIVATE
)
// Запис на данни
with(prefs.edit()) {
putString("username", "Анна")
putInt("age", 28)
putBoolean("isLoggedIn", true)
apply()
}
// Четене на данни
val username = prefs.getString("username", "")
val age = prefs.getInt("age", 0)
val isLoggedIn = prefs.getBoolean("isLoggedIn", false)
При използване на MODE_MULTI_PROCESS (остарял) SharedPreferences се синхронизира между процесите. Тази синхронизация обаче не гарантира атомарност и Google препоръчва избягване на използването на SharedPreferences в multi-process сценарии. За такива случаи е по-добре да използвате ContentProvider, Room с междупроцесен достъп или DataStore.
SharedPreferences предоставя набор от методи за четене на данни по ключ и интерфейс Editor за запис. Всеки метод за четене приема два параметъра: ключ и стойност по подразбиране, която се връща, ако ключът не бъде намерен. Стойността по подразбиране също определя типа на връщаната стойност: getString връща String, getInt връща Int и така нататък.
| Метод за четене | Метод за запис | Тип данни |
|---|---|---|
| getString | putString | String |
| getInt | putInt | Int |
| getBoolean | putBoolean | Boolean |
| getFloat | putFloat | Float |
| getLong | putLong | Long |
| getStringSet | putStringSet | Set<String> |
Editor — вътрешен обект на SharedPreferences, който събира промените в буфер. След въвеждане на всички промени, разработчикът извиква commit() (синхронен запис) или apply() (асинхронен запис). Разликата е критична: commit блокира текущата нишка до пълния запис на диска и връща boolean (успех/неуспех), докато apply извършва записа във фонова нишка и веднага връща контрола, но не връща резултат.
Препоръчва се използването на apply вместо commit във всички случаи, когато не е необходимо да се знае резултатът от записа. apply е по-бърз и не блокира UI нишката. commit трябва да се използва само когато е критично да се знае дали данните са запазени успешно, или при работа с multi-process режим. За премахване на отделни ключове се използва методът remove, за пълно изчистване — clear. Всички операции за премахване също се извършват чрез Editor.
// Множество промени - един apply
prefs.edit {
putString("theme", "dark")
putBoolean("notifications", false)
remove("old_key")
}
// Слушател за промяна на стойност
prefs.registerOnSharedPreferenceChangeListener { prefs, key ->
Log.d("TAG", "Ключът се промени: $key")
}
От Android 12 (API 31) нататък, SharedPreferences е допълнен с поддръжка за registerOnSharedPreferenceChangeListener с автоматично отписване чрез Lifecycle. Това позволява избягване на изтичания на памет, свързани със забравени слушатели. В по-стари версии разработчикът е длъжен ръчно да извиква unregisterOnSharedPreferenceChangeListener в onDestroy или onStop на компонента.
Въпреки широкото си разпространение, SharedPreferences не е универсално решение за всички сценарии за съхранение на данни в Android. В зависимост от обема на данните, изискванията за типова безопасност и производителност, Google препоръчва различни алтернативи, включени в Android Jetpack и стандартната библиотека на Android.
| Решение | Кога да се използва | Недостатъци |
|---|---|---|
| SharedPreferences | Малки настройки (до 100 ключа) | Няма типова безопасност, синхронно четене |
| DataStore | Настройки със средна сложност с корутини | Няма обратна съвместимост под API 14 |
| Room | Структурирани данни и списъци | Излишен за 3–5 настройки |
| EncryptedSharedPreferences | Чувствителни данни и токени | Зависимост от AndroidX Security |
DataStore — библиотека на Android Jetpack, представена от Google като заместител на SharedPreferences. Предлага два варианта: Preferences DataStore (ключ-стойност, като SharedPreferences) и Proto DataStore (типизирано съхранение чрез Protocol Buffers). DataStore използва корутини и Flow за асинхронна работа, гарантира типова безопасност и автоматично обработва миграции на версии. Google препоръчва DataStore за всички нови проекти.
Основното предимство на DataStore — асинхронност на ниво API. Всички операции за четене връщат Flow, а операциите за запис са suspend-функции. Това напълно елиминира блокирането на UI нишката, което е възможно при синхронно четене на SharedPreferences. Освен това DataStore гарантира консистентност на данните: записът се извършва в транзакция и при повреда всички промени се отменят.
Нека разгледаме практически пример: настройки на тема (светла/тъмна/системна) в Android приложение. Потребителят избира тема и изборът се запазва в SharedPreferences. При следващи стартирания на приложението темата се възстановява от запазените настройки. За реактивно актуализиране на интерфейса се използва наблюдение на промени чрез SharedPreferences.OnSharedPreferenceChangeListener.
Ще създадем клас ThemePreferences, който капсулира цялата работа с SharedPreferences за темата. Класът предоставя методи getTheme (четене), setTheme (запис) и observeTheme (наблюдение). Името на файла с настройки ще бъде „app_preferences" с режим MODE_PRIVATE. За удобство ключовете са изнесени в companion object като константи.
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())
}
}
}
}
В Activity или Fragment получаването на инстанция на ThemePreferences се осъществява чрез контекста на приложението. При инициализация се извиква getTheme за задаване на текущата тема. Когато потребителят избере нова тема, се извиква setTheme и чрез observeTheme интерфейсът се актуализира без рестартиране на Activity. Важно е да не забравите да се отпишете от слушателя в onDestroy, за да предотвратите изтичане на памет, особено ако Activity се пресъздава при промяна на конфигурацията.
За приложения с минимална целева версия Android 12+ се препоръчва използването на registerOnSharedPreferenceChangeListener заедно с LifecycleObserver. Това автоматично управлява абонамента и отписването при промяна на жизнения цикъл на компонента. За по-стари версии абонаментът и отписването трябва да се управляват ръчно, което е чест източник на грешки в производствени приложения, използващи SharedPreferences.
Често задавани въпроси
SharedPreferences директно поддържа само примитивни типове и Set<String>. За съхранение на обекти те трябва да бъдат сериализирани в JSON низ чрез Gson или Moshi, запазени чрез putString и десериализирани при четене. За сложни обекти с голям брой полета се препоръчва използването на Room вместо SharedPreferences с JSON сериализация.
Да, SharedPreferences е thread-safe. Всички операции за четене и запис са синхронизирани на ниво обект SharedPreferences и неговия Editor. Въпреки това, при използване на multi-process режим синхронизацията не е гарантирана. За конкурентен достъп от множество нишки в рамките на едно приложение, SharedPreferences е безопасен без допълнителни заключвания.
За пълно изчистване на всички данни от SharedPreferences, извикайте метода clear() на Editor и приложете промените чрез apply. Ако трябва да изтриете самия XML файл, използвайте deleteSharedPreferences(name) на контекста. Изчистването на данните на приложението чрез Настройки → Приложения → Изчистване на данни също премахва всички SharedPreferences файлове.
За нови проекти Google препоръчва DataStore като заместител на SharedPreferences. DataStore осигурява асинхронна работа с корутини, типова безопасност (Proto DataStore) и автоматични миграции. SharedPreferences трябва да се избира само за проекти с минимална версия под API 14 или когато е необходима бърза интеграция без допълнителни зависимости.
За криптиране на данни използвайте EncryptedSharedPreferences от библиотеката AndroidX Security. Тя автоматично криптира ключовете и стойностите с AES-256 GCM. Процесът на настройка е минимален: getSharedPreferences се заменя с EncryptedSharedPreferences.create с посочване на главния ключ от Android Keystore.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също