Jetpack DataStore patří mezi technologie pro perzistenci v Androidu, které používám ve chvíli, kdy aplikace potřebuje spolehlivé lokální ukládání preferencí, nastavení nebo menších strukturovaných dat.

Používám ho především jako moderní náhradu za SharedPreferences a jako součást Android architektury založené na Kotlinu, kde má být aplikační stav zpřístupněn reaktivně prostřednictvím Flow a aktualizován asynchronně pomocí coroutines.

Pro mě DataStore není jen místo pro uložení několika hodnot.

Je součástí způsobu, jakým navrhuji předvídatelný aplikační stav, který přežije restart procesu a zůstává synchronizovaný s uživatelským rozhraním.

Jak používám DataStore

DataStore používám například pro:

Konkrétní implementace závisí na tom, jak strukturovaná mají uložená data být.

Pro jednoduchá nastavení typu key-value často postačuje Preferences DataStore.

Pro silně typovaný strukturovaný stav mohu použít typovaný DataStore s explicitním serializačním formátem.

Náhrada SharedPreferences

Jedním z hlavních důvodů, proč používám DataStore, je modernější model perzistence oproti tradičním SharedPreferences.

DataStore je navržen kolem:

To mnohem lépe zapadá do moderní Android architektury než synchronní přístup k preferencím rozptýlený napříč aktivitami nebo composable funkcemi.

Místo ručního načítání hodnot při každém vytvoření obrazovky může aplikace sledovat perzistentní stav jako datový proud.

Preferences DataStore

Preferences DataStore je vhodný tehdy, když aplikace potřebuje jednoduché úložiště typu key-value.

Typickými příklady jsou:

Ukládané hodnoty zůstávají jednoduché, zatímco DataStore řeší jejich perzistenci asynchronně.

Používám jasně definované klíče a přímý přístup k DataStore obvykle skrývám za repository nebo komponentu nastavení místo toho, abych preference keys zpřístupňoval napříč celou aplikací.

Typovaný DataStore

Pokud má perzistentní stav smysluplnější strukturu, může typovaný DataStore nabídnout čistší model.

Místo správy nezávislých klíčů aplikace ukládá strukturovaný objekt.

To může zpřehlednit nastavení v situacích, kdy více vlastností logicky patří k sobě.

Typované ukládání zároveň poskytuje silnější kontrakt mezi perzistentními daty a aplikačním kódem.

Podle projektu může být serializace založená například na Protocol Buffers nebo jiném explicitním serializeru.

Proto DataStore

Proto DataStore je užitečný ve chvíli, kdy chci silně typovaný perzistentní stav s definovaným schématem.

Schéma může například popisovat nastavení:

UserSettings
├── dark_mode
├── notifications_enabled
├── preferred_language
└── selected_mode

Generované typy dělají uloženou strukturu explicitní.

Tím se snižuje riziko chyb způsobených:

U strukturovanější konfigurace tento přístup preferuji před stále větší kolekcí nesouvisejících preference keys.

Kotlin Coroutines

DataStore je navržen pro asynchronní operace.

Při čtení a aktualizaci perzistentních dat používám Kotlin coroutines, aby práce s úložištěm neblokovala hlavní vlákno aplikace.

To přirozeně zapadá do zbytku Android architektury založené na coroutines.

Repository pro nastavení může zpřístupňovat perzistentní hodnoty, zatímco obrazovky se soustředí pouze na vykreslování stavu.

Kotlin Flow

Flow je jedním z hlavních důvodů, proč DataStore tak dobře zapadá do moderního vývoje Android aplikací.

Uložená data lze zpřístupnit jako Flow, takže zbytek aplikace může automaticky reagovat na změny hodnot.

Koncepčně:

DataStore
→ Flow
→ ViewModel
→ UI State
→ Jetpack Compose

Když se preference změní, nová hodnota se propaguje celou architekturou.

UI nemusí úložiště opakovaně pollovat.

DataStore s Jetpack Compose

DataStore funguje obzvlášť dobře s Jetpack Compose.

Perzistentní nastavení obvykle zpřístupňuji přes ViewModel nebo jinou stavovou vrstvu a nechávám Compose sledovat výsledný stav.

Pokud například uživatel změní téma aplikace, může se tato hodnota zapsat do DataStore.

Změna následně projde přes ViewModel a způsobí automatickou recomposition příslušných composable funkcí.

Vzniká tak čisté oddělení:

Compose UI
→ akce uživatele
→ ViewModel / repository
→ DataStore

DataStore
→ Flow
→ ViewModel
→ Compose UI

UI nemusí vědět, jak je nastavení fyzicky uložené.

Repository pattern

Přístup k DataStore obecně držím za samostatným repository nebo abstrakcí pro nastavení.

Místo toho, aby nesouvisející části aplikace přímo četly a zapisovaly preference keys, zpřístupňuji operace typu:

setDarkMode()
setPreferredLanguage()
setNotificationsEnabled()

a pozorovatelný stav, například:

settingsFlow

Perzistentní vrstva tak má jasně definované rozhraní.

Zároveň se tím usnadňují případné budoucí změny způsobu ukládání dat.

Single Source of Truth

U perzistentních nastavení preferuji jeden jasný zdroj pravdy.

Nezávislé duplikování stejného stavu v:

může snadno vytvářet nekonzistence.

Pokud DataStore reprezentuje perzistentní stav, dočasné UI reprezentace by z něj měly vycházet nebo se s ním synchronizovat prostřednictvím jasně definované architektury.

Díky tomu je chování aplikace snazší pochopit.

Výchozí hodnoty

Aplikace potřebuje předvídatelné chování ještě předtím, než byla konkrétní hodnota vůbec někdy uložená.

Pro perzistentní preference proto definuji explicitní výchozí hodnoty.

Například:

darkMode = false
notificationsEnabled = true

Tím je zajištěno konzistentní chování při prvním spuštění místo závislosti na null nebo nedefinovaném stavu.

Aktualizace dat

Aktualizace v DataStore jsou transakční.

Používám jeho aktualizační mechanismy místo ručního čtení souboru, změny hodnot a následného zápisu celého stavu zpět z nesouvisejícího kódu.

U strukturovaného DataStore transformační operace převádí existující hodnotu na nový immutable stav.

Tím se snižuje riziko konfliktních nebo částečně zapsaných aktualizací.

Immutable stav

Pro nastavení aplikace preferuji immutable modely.

Místo změny sdíleného objektu nastavení přímo na místě vytváří aktualizace nový stav.

To přirozeně zapadá do:

Immutable stav usnadňuje sledování datového toku a omezuje nenápadné problémy se synchronizací.

Zpracování chyb

Perzistentní úložiště může selhat.

Proto zohledňuji chyby, jako jsou:

Aplikace by tyto situace měla řešit záměrně místo pádu při spuštění kvůli tomu, že nelze přečíst jeden soubor s preferencemi.

Kde je to vhodné, používám bezpečné výchozí hodnoty nebo explicitní zpracování poškozených dat.

Migrace ze SharedPreferences

Existující Android aplikace mohou už preference ukládat pomocí SharedPreferences.

Při modernizaci takového projektu mohu existující hodnoty migrovat do DataStore místo toho, aby uživatelé přišli o svá nastavení.

Migrační strategie musí zachovat význam původních hodnot a správně je namapovat do nového datového modelu.

Po úspěšné migraci může aplikace dál používat DataStore jako primární mechanismus perzistence.

Vývoj schématu

Typovaný perzistentní stav se může vyvíjet spolu s přibývající funkcionalitou aplikace.

Například raná verze může obsahovat:

theme
language

zatímco pozdější verze přidá:

notifications
default_screen
accessibility_preferences

Výchozí hodnoty a serializaci navrhuji tak, aby bylo možné bezpečně interpretovat i starší uložený stav.

Perzistentní aplikační data by se měla vyvíjet společně se softwarem místo toho, aby se stala překážkou budoucího vývoje.

DataStore vs. Room

DataStore a Room používám pro rozdílné typy problémů.

DataStore je vhodný pro:

Room je vhodnější pro:

Nesnažím se z DataStore dělat náhradu relační databáze.

Volba správné perzistentní vrstvy udržuje architekturu jednodušší.

DataStore vs. SQLite

SQLite poskytuje relační databázový engine.

DataStore poskytuje lehký perzistentní aplikační stav.

Pokud potřebuji:

users → records → categories → history

obvykle použiji Room nebo SQLite.

Pokud potřebuji:

theme = dark
language = cs
notifications = enabled

DataStore je zpravidla čistší řešení.

Technologie pro ukládání dat by měla odpovídat složitosti samotných dat.

Lokální aplikační stav

DataStore je užitečný pro hodnoty, které musí přežít ukončení procesu, aniž by vyžadovaly vzdálený server.

Aplikace si tak může pamatovat například:

Tím se zlepšuje kontinuita mezi jednotlivými spuštěními aplikace.

Offline chování

Protože je DataStore lokální, aplikace nepotřebuje síťové připojení pro přístup k těmto nastavením.

To je užitečné pro Android aplikace, které se mají chovat předvídatelně i v situacích, kdy jsou:

Základní preference aplikace by obecně neměly být zbytečně závislé na vzdálené službě.

Stav monetizace

V aplikacích s monetizací mohu DataStore používat pro cachovaný lokální stav související s uživatelskou zkušeností.

Může si například pamatovat, že aplikace dříve zaznamenala premium nebo remove-ads oprávnění.

Pečlivě ale rozlišuji mezi:

Skutečným zdrojem pravdy o nákupu může stále být Google Play Billing nebo backend.

DataStore může zlepšit chování při spuštění aplikace, neměl by ale automaticky nahrazovat ověření nákupu.

Preference reklam

DataStore může být součástí stavu souvisejícího s monetizací, například:

Souhlas s reklamou citlivý z hlediska soukromí by ale stále měl respektovat požadavky příslušné consent platformy a SDK.

Obecný lokální příznak nepoužívám jako náhradu skutečného consent systému.

Obrazovky nastavení

DataStore je obzvlášť užitečný jako podklad pro obrazovky nastavení aplikace.

Uživatel může změnit volbu a tato změna se může:

  1. okamžitě aktualizovat,
  2. lokálně uložit,
  3. propagovat do zbytku aplikace.

S Flow a Compose může stejný stav řídit jak rozhraní nastavení, tak funkci, kterou dané nastavení ovlivňuje.

Tím se vyhýbám udržování duplicitního stavu pro stejnou preferenci.

Aplikační architektura

Typická architektura, kterou používám, může vypadat takto:

Jetpack Compose
      ↓
ViewModel
      ↓
Settings Repository
      ↓
DataStore

a perzistentní změny se propagují zpět opačným směrem:

DataStore
      ↓
Flow
      ↓
ViewModel
      ↓
Compose UI

Každá vrstva má jasně definovanou odpovědnost.

Díky tomu lze mechanismus perzistence případně nahradit, aniž by byla každá obrazovka přímo svázaná s DataStore.

Dependency Injection

Ve větších Android aplikacích lze repository postavené nad DataStore poskytovat prostřednictvím dependency injection.

Tím se zabrání vytváření instancí úložiště na náhodných místech v codebase a pomáhá to udržet konzistentní aplikační scope.

Zároveň se zjednodušuje testování, protože perzistentní abstrakci lze nahradit testovací implementací.

Jedna instance DataStore na soubor

Vlastnictví instancí DataStore držím explicitní.

Více nezávislých instancí směřujících na stejný soubor úložiště může způsobit nesprávné chování.

Centralizované vytváření na úrovni aplikace nebo dependency injection tomuto problému předchází a jasně definuje životní cyklus perzistentní vrstvy.

Testování

Oddělení DataStore za abstrakci zlepšuje také testování.

Aplikační logiku lze testovat proti:

Funkcionalitu tak lze testovat bez zbytečné závislosti na skutečném perzistentním prostředí.

DataStore a Android lifecycle

Perzistentní preference by neměly záviset na existenci konkrétní Activity nebo obrazovky.

DataStore proto držím na aplikační vrstvě místo jeho vytváření uvnitř jednotlivých UI komponent.

Stav díky tomu odolává:

Rozhraní může zmizet a být znovu vytvořeno, zatímco podkladové perzistentní nastavení zůstává zachované.

Výkon

DataStore je určen pro relativně malé množství aplikačního stavu.

Ukládané struktury udržuji úzce zaměřené a nepoužívám ho jako kontejner pro rozsáhlé kolekce aplikačních záznamů.

Tím zůstávají čtení i aktualizace předvídatelné.

Pokud data narostou do podoby, která vyžaduje složité dotazy nebo časté částečné změny, přesouvám tuto odpovědnost do Room nebo jiné databázové vrstvy.

Bezpečnost

DataStore by neměl být automaticky považován za bezpečné úložiště tajných údajů.

Pokud aplikace potřebuje ukládat citlivé přístupové údaje nebo kryptografická tajemství, zvažuji mechanismy navržené přímo pro tento účel.

DataStore je vhodný pro aplikační stav a preference.

Strategii ukládání by měly určovat bezpečnostní požadavky konkrétních dat.

Zálohování a migrace zařízení

Perzistentní lokální data mohou interagovat s mechanismy Androidu pro zálohování a přenos mezi zařízeními.

Zvažuji, zda mají být konkrétní nastavení zachována v situacích, kdy uživatel:

Ne každý typ lokálního stavu musí nutně používat stejnou politiku zálohování.

DataStore v mém technologickém stacku

DataStore běžně používám společně s technologiemi, jako jsou:

V rámci širší Android architektury poskytuje lehkou vrstvu perzistentního aplikačního stavu.

Proč používám DataStore

DataStore používám proto, že moderní Android aplikace potřebují předvídatelný způsob ukládání menšího množství stavu bez blokování UI nebo rozptylování synchronního přístupu k preferencím napříč codebasem.

Jeho kombinace asynchronního ukládání, Kotlin Flow a transakčních aktualizací přirozeně zapadá do moderního vývoje Android aplikací.

Nejdůležitější ale je, že ho používám pro typ dat, pro který byl navržen.

Preference a lehký aplikační stav patří do DataStore.

Komplexní relační aplikační data patří do Room nebo jiné databáze.

Jasné zachování této hranice vede k jednodušším a udržitelnějším Android aplikacím.

Jetpack DataStore is one of the Android persistence technologies I use when an application needs reliable local storage for preferences, settings or small structured datasets.

I use it primarily as a modern replacement for SharedPreferences and as part of Kotlin-based Android architecture where application state should be exposed reactively through Flow and updated asynchronously through coroutines.

For me, DataStore is not simply a place to save a few values.

It is part of how I design predictable application state that survives process restarts and remains synchronized with the user interface.

How I use DataStore

I use DataStore for information such as:

The exact implementation depends on how structured the stored information needs to be.

For simple key-value settings, Preferences DataStore is often sufficient.

For strongly typed structured state, I can use typed DataStore with an explicit serialization format.

Replacing SharedPreferences

One of the main reasons I use DataStore is that it provides a more modern persistence model than traditional SharedPreferences.

DataStore is designed around:

This fits much better with modern Android architecture than synchronous preference access scattered throughout activities or composables.

Instead of manually reading values whenever a screen is created, the application can observe persistent state as a stream.

Preferences DataStore

Preferences DataStore is useful when an application needs straightforward key-value storage.

Typical examples include:

The stored values remain simple, while DataStore handles persistence asynchronously.

I use clearly defined keys and normally hide direct DataStore access behind a repository or settings component rather than exposing preference keys throughout the application.

Typed DataStore

When the persisted state has a more meaningful structure, typed DataStore can provide a cleaner model.

Instead of managing independent keys, the application stores a structured object.

This can make settings easier to reason about when multiple properties logically belong together.

Typed storage also provides a stronger contract between persisted data and application code.

Depending on the project, serialization can be based on formats such as Protocol Buffers or another explicit serializer.

Proto DataStore

Proto DataStore is useful when I want strongly typed persisted state with a defined schema.

A schema can describe settings such as:

UserSettings
├── dark_mode
├── notifications_enabled
├── preferred_language
└── selected_mode

The generated types make the stored structure explicit.

This reduces mistakes caused by:

For more structured configuration, I prefer this approach over an increasingly large collection of unrelated preference keys.

Kotlin Coroutines

DataStore is designed around asynchronous operations.

I use Kotlin coroutines when reading and updating persisted data so storage work does not block the application’s main thread.

This fits naturally with other coroutine-based Android architecture.

A settings repository can expose persistent values while screens remain focused on rendering state.

Kotlin Flow

Flow is one of the strongest reasons DataStore fits well into modern Android development.

Stored data can be exposed as a Flow, allowing the rest of the application to react automatically when a value changes.

Conceptually:

DataStore
→ Flow
→ ViewModel
→ UI State
→ Jetpack Compose

When a preference changes, the updated value propagates through the architecture.

The UI does not need to repeatedly poll storage.

DataStore with Jetpack Compose

DataStore works particularly well with Jetpack Compose.

I normally expose persistent settings through a ViewModel or state layer and let Compose observe the resulting state.

For example, a user changing the application theme can update DataStore.

That update flows through the ViewModel and causes the relevant composables to recompose automatically.

This creates a clean separation:

Compose UI
→ user action
→ ViewModel / repository
→ DataStore

DataStore
→ Flow
→ ViewModel
→ Compose UI

The UI does not need to know how the setting is physically stored.

Repository Pattern

I generally keep DataStore access behind a dedicated repository or settings abstraction.

Instead of allowing unrelated parts of the application to read and write preference keys directly, I expose operations such as:

setDarkMode()
setPreferredLanguage()
setNotificationsEnabled()

and observable state such as:

settingsFlow

This gives the persistence layer a clear interface.

It also makes future changes to storage easier.

Single Source of Truth

For persistent settings, I prefer having one clear source of truth.

Duplicating the same state independently in:

can easily create inconsistencies.

Where DataStore represents the persistent state, temporary UI representations should derive from it or synchronize with it through a clearly defined architecture.

This makes application behavior easier to reason about.

Default Values

An application needs predictable behavior before a setting has ever been stored.

I define explicit default values for persistent preferences.

For example:

darkMode = false
notificationsEnabled = true

This ensures the application behaves consistently on first launch rather than depending on nullable or undefined state.

Updating Data

DataStore updates are transactional.

I use its update mechanisms rather than manually reading a file, modifying values and writing the entire state back through unrelated code.

For structured DataStore, the update operation transforms the existing value into a new immutable state.

This reduces the risk of conflicting or partially written updates.

Immutable State

I prefer immutable models for application settings.

Instead of mutating a shared settings object in place, an update produces a new state.

This fits naturally with:

Immutable state makes data flow easier to follow and reduces subtle synchronization problems.

Error Handling

Persistent storage can fail.

I therefore consider errors such as:

The application should handle these conditions deliberately rather than crashing during startup because one preference file cannot be read.

Where appropriate, I provide safe defaults or explicit corruption handling.

Migration from SharedPreferences

Existing Android applications may already store preferences using SharedPreferences.

When modernizing such a project, I can migrate existing values into DataStore rather than forcing users to lose their settings.

A migration strategy needs to preserve the meaning of the old values and map them correctly into the new data model.

After successful migration, the application can continue using DataStore as the primary persistence mechanism.

Schema Evolution

Typed persisted state may evolve as the application gains new functionality.

For example, an early version may contain:

theme
language

while a later version adds:

notifications
default_screen
accessibility_preferences

I design defaults and serialization so older stored state can still be interpreted safely.

Persistent application data should evolve together with the software rather than becoming an obstacle to future development.

DataStore vs Room

I use DataStore and Room for different problems.

DataStore is a good fit for:

Room is more appropriate for:

I do not try to turn DataStore into a replacement for a relational database.

Choosing the correct persistence layer keeps the architecture simpler.

DataStore vs SQLite

SQLite provides a relational database engine.

DataStore provides lightweight persisted application state.

If I need:

users → records → categories → history

I would normally use Room or SQLite.

If I need:

theme = dark
language = cs
notifications = enabled

DataStore is usually the cleaner solution.

The storage technology should match the complexity of the data.

Local Application State

DataStore is useful for values that need to survive process termination without requiring a remote server.

This allows an application to remember behavior such as:

That improves continuity between application sessions.

Offline Behavior

Because DataStore is local, the application does not need a network connection to access these settings.

This is useful for Android applications that should behave predictably even when:

Basic application preferences should generally not depend unnecessarily on a remote service.

Monetization State

In applications with monetization, I may use DataStore for cached local state related to the user experience.

For example, it can remember that the application previously observed a premium or remove-ads entitlement.

However, I distinguish carefully between:

Google Play Billing or a backend may still be the real source of truth for a purchase.

DataStore can improve startup behavior, but it should not automatically replace purchase verification.

Ad Preferences

DataStore can also participate in monetization-related state such as:

Privacy-sensitive advertising consent itself should still follow the requirements of the relevant consent platform and SDK.

I do not use a generic local flag as a substitute for the actual consent system.

Settings Screens

DataStore is particularly useful behind application settings screens.

A user can change an option and have that choice:

  1. update immediately,
  2. persist locally,
  3. propagate to the rest of the application.

With Flow and Compose, the same state can control both the settings interface and the feature affected by that setting.

This avoids maintaining duplicate state for the same preference.

Application Architecture

A typical architecture I use may look like:

Jetpack Compose
      ↓
ViewModel
      ↓
Settings Repository
      ↓
DataStore

and persistent changes flow back in the opposite direction:

DataStore
      ↓
Flow
      ↓
ViewModel
      ↓
Compose UI

Each layer has a clear responsibility.

This makes the persistence mechanism replaceable without coupling every screen directly to DataStore.

Dependency Injection

In larger Android applications, the DataStore-backed repository can be supplied through dependency injection.

This avoids creating storage instances in random parts of the codebase and helps maintain a consistent application scope.

It also makes testing easier because the persistence abstraction can be replaced with a test implementation.

One DataStore Instance per File

I keep ownership of DataStore instances explicit.

Multiple independent instances pointing to the same storage file can create incorrect behavior.

Centralizing creation at the application or dependency-injection level avoids this problem and makes the lifecycle of the storage layer clear.

Testing

Separating DataStore behind an abstraction also improves testing.

Application logic can be tested against:

This allows features to be tested without depending unnecessarily on the real persistent environment.

DataStore and Android Lifecycle

Persistent preferences should not depend on a particular Activity or screen existing.

I therefore keep DataStore at an application-oriented layer rather than creating it inside individual UI components.

This makes state resilient to:

The interface can disappear and be recreated while the underlying persistent settings remain intact.

Performance

DataStore is intended for relatively small amounts of application state.

I keep stored structures focused and avoid using it as a container for large collections of application records.

This keeps reads and updates predictable.

If the data grows into something that needs complex queries or frequent partial modification, I move that responsibility to Room or another database layer.

Security

DataStore should not automatically be treated as secure secret storage.

If an application needs to store sensitive credentials or cryptographic secrets, I consider storage mechanisms designed specifically for that purpose.

DataStore is appropriate for application state and preferences.

The security requirements of the data should determine the storage strategy.

Backup and Device Migration

Persistent local data may interact with Android backup and device-to-device transfer behavior.

I consider whether particular settings should be preserved when the user:

Not every piece of local state should necessarily follow the same backup policy.

DataStore in My Technology Stack

I commonly use DataStore alongside technologies such as:

It provides the lightweight persistent state layer within a broader Android application architecture.

Why I Use DataStore

I use DataStore because modern Android applications need a predictable way to persist small amounts of state without blocking the UI or scattering synchronous preference access throughout the codebase.

Its combination of asynchronous storage, Kotlin Flow and transactional updates fits naturally with modern Android development.

Most importantly, I use it for the type of data it is designed for.

Preferences and lightweight application state belong in DataStore.

Complex relational application data belongs in Room or another database.

Keeping that boundary clear results in simpler, more maintainable Android applications.