Room против SharedPreferences против DataStore: какое хранилище для чего
Эти три сравнивают так, будто вы выбираете один для всего приложения. На практике они решают разные проблемы и часто сосуществуют.
SharedPreferences: старое значение по умолчанию, постепенно уходящее
Простое хранилище ключ-значение, исторически значение по умолчанию для небольших настроек. Известные ограничения: его легко неправильно использовать с синхронным чтением в главном потоке (вызывая подёргивания), не предлагает безопасность типов на этапе компиляции, и Google теперь рекомендует DataStore как его замену для нового кода.
DataStore: современная замена для простых данных ключ-значение
DataStore выпускается в двух вариантах — Preferences DataStore (похожая форма на SharedPreferences, но асинхронная и безопаснее) и Proto DataStore (полностью типизированная, использующая protocol buffers). В любом случае, он построен для той же задачи, что делал SharedPreferences — простые настройки и флаги — сделано правильно: асинхронно по умолчанию, с определённой моделью согласованности данных.
val Context.dataStore by preferencesDataStore(name = "settings")
val THEME_KEY = stringPreferencesKey("theme")
suspend fun saveTheme(context: Context, theme: String) {
context.dataStore.edit { prefs -> prefs[THEME_KEY] = theme }
}Room: для реально структурированных, запрашиваемых данных
Room — это полная абстракция SQLite — слой в стиле ORM для структурированных данных с отношениями, запросами и (через coroutines/Flow) реактивными обновлениями. Это правильный инструмент, когда у вас есть подлинная коллекция структурированных записей: список сохранённых элементов, кэшированные результаты API, всё, что вы бы естественно описали как таблицу со строками.
@Entity
data class Note(@PrimaryKey val id: Int, val title: String, val body: String)
@Dao
interface NoteDao {
@Query("SELECT * FROM Note ORDER BY id DESC")
fun getAll(): Flow<List<Note>>
}Сравнительная таблица
| SharedPreferences | DataStore | Room | |
|---|---|---|---|
| Лучше всего для | Устаревшего кода (избегайте для нового) | Простых настроек/флагов | Структурированных, запрашиваемых данных |
| Безопасность типов | Нет | Строгая (особенно Proto DataStore) | Строгая |
| Асинхронность по умолчанию | Нет (частый источник багов) | Да | Да (с coroutines/Flow) |
| Обрабатывает отношения/запросы | Нет | Нет | Да |
| Текущая рекомендация Google | Постепенно уходит | Да, для данных ключ-значение | Да, для структурированных данных |
Практическое правило
Если это горстка флагов или предпочтений (тема, был ли показан онбординг, переключатель уведомлений), используйте DataStore. Если это подлинная коллекция структурированных записей, которую вы бы запрашивали, фильтровали или связывали с другими данными, используйте Room. Избегайте начинать новый код с SharedPreferences — стоимость миграции на DataStore позже можно избежать, просто начав оттуда.
Это решение напрямую связано с более широким вопросом архитектуры — см. наше руководство по проектированию Android-архитектуры, избегающей переписываний, о том, как выборы слоя данных вписываются в общую картину.
Частые вопросы
Устарел ли SharedPreferences?
Формально не удалён, но Google рекомендует DataStore как его замену для нового кода — у SharedPreferences есть известные проблемы (синхронный API в главном потоке, отсутствие строгой типизации), которые DataStore был построен решить специально.
Могу ли я использовать больше одного из них в одном приложении?
Да, и это распространено — Room для структурированных данных (список элементов, записи пользователей), DataStore для простых настроек приложения, используются вместе, а не как конкурирующие варианты для одной задачи.
Нужен ли мне Room для простого экрана настроек?
Нет — Room построен для структурированных, запрашиваемых данных с отношениями. Горстка пользовательских предпочтений (выбор темы, переключатель уведомлений) — именно то, для чего вместо этого предназначен DataStore.