Android Jetpack je důležitou součástí způsobu, jakým strukturuji moderní Android aplikace.
Poskytuje soubor knihoven a architektonických komponent, které omezují množství boilerplate kódu, standardizují běžné aplikační vzory a usnadňují tvorbu softwaru, který se chová konzistentně napříč verzemi Androidu, zařízeními a různými formáty obrazovek.
Jetpack používám jako architektonický základ kolem technologií, jako jsou Kotlin a Jetpack Compose.
Jetpack pro mě není jedna konkrétní knihovna.
Je to ekosystém, který propojuje životní cyklus aplikace, stav, navigaci, perzistenci, práci na pozadí a architekturu uživatelského rozhraní do uceleného modelu vývoje pro Android.
Jak Android Jetpack používám
Knihovny Jetpack používám například pro:
- architekturu uživatelského rozhraní,
- stav respektující životní cyklus,
- navigaci,
- lokální perzistenci,
- práci na pozadí,
- uživatelská nastavení,
- správu aplikačního stavu,
- architekturu se správou závislostí,
- adaptivní layouty,
- kompatibilitu napříč zařízeními.
Podle konkrétní aplikace může jít například o technologie:
- Jetpack Compose,
- ViewModel,
- Navigation,
- Room,
- DataStore,
- WorkManager,
- Lifecycle,
- Activity APIs.
Jednotlivé komponenty vybírám podle skutečných potřeb aplikace, ne jen proto, že patří do Jetpacku.
Moderní architektura Android aplikací
Jednou z největších výhod Jetpacku je, že nabízí zavedené postupy pro strukturování Android aplikací.
Místo umisťování aplikační logiky přímo do Activities, obrazovek nebo composables preferuji jasně oddělené vrstvy s přesně definovanými odpovědnostmi.
Typická architektura může vypadat například takto:
Jetpack Compose
↓
ViewModel
↓
Repository
↓
Room / DataStore / REST API
UI vykresluje stav.
ViewModel koordinuje stav obrazovky a uživatelské akce.
Repositories zajišťují přístup k aplikačním datům.
Perzistentní a síťové vrstvy řeší ukládání dat a vzdálenou komunikaci.
Díky tomu je codebase snazší testovat, rozšiřovat a udržovat.
Jetpack Compose
Jetpack Compose je hlavní moderní UI toolkit, který používám pro nativní Android rozhraní.
Compose umožňuje deklarativně popsat uživatelské rozhraní na základě aplikačního stavu.
Místo ručního upravování jednotlivých view aplikace zpřístupní stav a UI na jeho změny reaguje.
Koncepčně:
stav aplikace
→ Compose
→ vykreslené UI
Když se stav změní, Compose aktualizuje ty části rozhraní, které změnu skutečně potřebují.
Tento přístup přirozeně zapadá do zbytku architektury Jetpacku.
ViewModel
ViewModel je jednou z klíčových komponent, které používám pro správu aplikačního stavu na úrovni obrazovky.
ViewModel může přežít změny konfigurace a pomáhá držet aplikační logiku mimo UI vrstvu.
ViewModely používám k tomu, aby:
- zpřístupňovaly stav UI,
- zpracovávaly uživatelské akce,
- komunikovaly s repositories,
- koordinovaly asynchronní operace,
- držely logiku obrazovky mimo composables.
Preferuji ViewModely na úrovni konkrétních obrazovek před předáváním instancí ViewModelu hluboko do hierarchie UI.
Nižší composables dostávají pouze data a callbacky, které skutečně potřebují.
Díky tomu se snáze testují a znovu používají.
Správa stavu
Moderní Android aplikace jsou do značné míry řízené stavem.
Preferuji explicitní stavové modely před množstvím nesouvisejících mutable proměnných.
Obrazovka může například zpřístupňovat jeden strukturovaný stav s hodnotami jako:
loading
data
error
selectedItem
userPreferences
Rozhraní se následně vykresluje z tohoto stavu.
Chování aplikace je díky tomu výrazně snazší pochopit.
Kotlin Flow
Flow přirozeně zapadá do architektury aplikací založených na Jetpacku.
Používám jej pro reaktivní datové proudy, například:
- výsledky z databáze,
- uživatelská nastavení,
- aplikační stav,
- stav synchronizace.
Běžná cesta dat může vypadat takto:
Room / DataStore
↓
Flow
↓
ViewModel
↓
Compose UI
Když se podkladová data změní, aktualizovaný stav se může automaticky propagovat napříč aplikací.
Práce se životním cyklem
Android aplikace mají komplexní životní cyklus.
Activities, cíle navigace i UI komponenty mohou být vytvářeny a rušeny podle toho, jak se uživatel pohybuje aplikací nebo jak operační systém spravuje prostředky.
Jetpack poskytuje nástroje respektující životní cyklus, které pomáhají zabránit tomu, aby práce pokračovala v kontextu, kde už nedává smysl.
Používám sběr stavu respektující životní cyklus a architekturu navrženou tak, aby aplikace správně reagovala na:
- navigaci,
- změny konfigurace,
- přechody na pozadí,
- znovuvytvoření Activity.
Práce se životním cyklem by měla být součástí architektury, ne souborem jednotlivých dodatečných oprav.
Navigation
Pro strukturování přechodů mezi obrazovkami aplikace používám komponentu Jetpack Navigation.
U aplikací vytvořených kompletně v Compose poskytuje Navigation Compose navigaci mezi composable destinations. Současná doporučení pro Android ji odlišují od navigace založené na Fragmentech u aplikací, které stále používají Views nebo kombinovanou architekturu Views a Compose.
Definice navigace držím odděleně od vizuální implementace jednotlivých obrazovek.
Díky tomu jsou routy i tok aplikace snáze pochopitelné.
Stav navigace
Navigace může mezi jednotlivými destinations přenášet informace, ale vyhýbám se předávání zbytečně velkých objektů prostřednictvím navigačních argumentů.
Preferuji předávání stabilních identifikátorů a následné načtení potřebných dat v cílové obrazovce prostřednictvím jejího ViewModelu nebo repository.
Například:
projectId = 42
je obvykle čistší řešení než předávat celou instanci projektu navigační vrstvou.
Navigační kontrakty tak zůstávají malé a předvídatelné.
Room
Room je perzistentní vrstva Jetpacku, kterou používám pro strukturovaná relační aplikační data.
Staví nad SQLite a poskytuje:
- entities,
- DAOs,
- validaci dotazů,
- migrace,
- integraci s coroutines,
- podporu Flow.
Room používám například pro:
- historie,
- kolekce záznamů,
- offline cache,
- strukturovaná data vytvářená uživatelem.
Room přirozeně zapadá do aplikační architektury založené na repository patternu.
DataStore
DataStore používám pro lehčí perzistentní stav, například:
- preference,
- nastavení,
- vybrané režimy,
- stav onboardingu.
DataStore je navržen kolem asynchronního přístupu a Flow.
Držím jej v datové vrstvě namísto přímého čtení nebo zápisu preferencí z composables. Tento přístup odpovídá současným doporučením pro architekturu Android aplikací.
Pro složitá relační data používám Room.
Jasné oddělení těchto odpovědností zjednodušuje perzistentní vrstvu aplikace.
WorkManager
Některé úlohy musí pokračovat nebo být opakovány nezávisle na aktuálně zobrazené obrazovce.
Pro tento typ práce Jetpack poskytuje WorkManager.
Mezi typické příklady použití patří:
- synchronizace na pozadí,
- odložené uploady,
- pravidelná údržba,
- zpracování podporující opakování po selhání.
Práci na pozadí používám pouze tehdy, když ji daný úkol skutečně potřebuje.
Ne každá asynchronní operace patří do WorkManageru.
Běžné operace na úrovni obrazovky je často vhodnější řešit pomocí coroutines a ViewModelů.
Spolehlivá práce na pozadí
Spouštění úloh na pozadí v Androidu musí počítat například s:
- ukončením procesu,
- dostupností připojení,
- omezeními baterie,
- stavem zařízení.
Robustní úloha na pozadí nemůže předpokládat, že proces aplikace zůstane neomezeně dlouho spuštěný.
Proto vyžaduje trvalé plánování práce jinou architekturu než jednoduchá coroutine spuštěná z UI obrazovky.
Activity APIs
Jetpack poskytuje také moderní API pro běžnou funkcionalitu na úrovni Activity.
Patří sem například vzory pro:
- výsledky z Activities,
- oprávnění,
- integraci životního cyklu.
V Compose aplikacích preferuji tato moderní API před ručním udržováním starších vzorů založených na velkém množství callbacků.
Cílem je udržet interakci s platformou Android předvídatelnou a respektující životní cyklus.
Aplikační stav vs. stav UI
Rozlišuji mezi různými typy stavu.
Například:
Stav UI:
dialogOpen
selectedTab
currentTextField
Aplikační stav:
currentUser
databaseRecords
premiumEntitlement
savedSettings
Ne každá hodnota musí být uložena ve ViewModelu nebo databázi.
Volba správného vlastníka a správné životnosti stavu pomáhá předcházet zbytečné komplexitě.
State hoisting
V Compose používám state hoisting, tedy přesouvání stavu na úroveň, kde jej lze vhodně řídit.
Znovupoužitelný composable by měl obvykle dostávat:
- aktuální stav,
- callbacky událostí.
Například:
SettingsSwitch(
checked = state.notificationsEnabled,
onCheckedChange = onNotificationsChanged
)
Komponenta nemusí vědět, zda stav ve výsledku pochází z DataStore, Room nebo backendu.
Tím se zlepšuje znovupoužitelnost i testovatelnost.
Coroutines
Kotlin coroutines jsou hluboce integrovány do moderní architektury Jetpacku.
Používám je pro asynchronní práci, například:
- databázové operace,
- API volání,
- aktualizace perzistentních dat,
- výpočty na pozadí.
Vlastnictví coroutine scope udržuji explicitní.
Operace na úrovni obrazovky může patřit do scope ViewModelu, zatímco práce na úrovni celé aplikace nebo trvalá práce může vyžadovat jiný mechanismus.
Repository pattern
Repositories představují užitečnou hranici mezi aplikační logikou a datovými zdroji.
Repository může kombinovat například:
- Room,
- DataStore,
- REST API,
- lokální cache,
- další služby.
Například:
ViewModel
↓
Repository
↙ ↘
Room REST API
ViewModel nemusí vědět, odkud přesně informace pochází.
Datová vrstva se tak může vyvíjet bez nutnosti měnit každou obrazovku.
Single Source of Truth
U důležitého stavu preferuji aplikace s jasně definovaným jediným zdrojem pravdy.
U offline-first funkcionality to může být Room.
U uživatelských preferencí to může být DataStore.
U přechodného stavu obrazovky to může být ViewModel.
Více nezávislých kopií stejného stavu se může snadno dostat do vzájemného nesouladu.
Jasně definovaný model vlastnictví toto riziko snižuje.
Offline-first architektura
Komponenty Jetpacku dobře fungují v aplikacích, které musí zůstat použitelné i bez trvalého připojení k internetu.
Typická offline-first architektura může vypadat takto:
REST API
↓
Repository
↓
Room
↓
Flow
↓
ViewModel
↓
Compose
UI čte data z lokálně perzistentního stavu.
Vzdálená synchronizace tento stav aktualizuje, jakmile je k dispozici připojení.
To vytváří plynulejší uživatelský zážitek i na nespolehlivých mobilních sítích.
Adaptivní rozhraní
Android aplikace dnes běží na širokém spektru různých zařízení a formátů.
Jetpack zahrnuje knihovny a Compose komponenty navržené pro adaptivní rozhraní, která dokážou reagovat na různé velikosti displejů.
Android aplikace nenavrhuji pro jedno pevně dané rozlišení telefonu.
Layouty by měly zůstat použitelné napříč:
- telefony,
- tablety,
- skládacími zařízeními,
- různými orientacemi obrazovky.
Současná doporučení Jetpacku výslovně zahrnují adaptivní komponenty Material 3 pro různé velikosti displejů.
Změny konfigurace
Android může znovu vytvořit části aplikace například kvůli:
- změně orientace,
- aktualizaci konfigurace,
- správě systémových prostředků.
Stav navrhuji tak, aby se důležité informace obrazovky při běžném znovuvytvoření omylem neztratily.
ViewModel je vhodný pro stav, který má přežít změny konfigurace, zatímco perzistentní úložiště používám tam, kde musí informace přežít ukončení procesu nebo restart aplikace.
Ukončení procesu
ViewModel není trvalé úložiště.
Pokud je proces ukončen, stav uložený pouze v paměti může zmizet.
Pro stav, který musí přežít znovuvytvoření procesu, zvažuji například:
- SavedStateHandle,
- Room,
- DataStore.
Správná volba závisí na tom, zda jde o dočasný stav UI, nebo skutečně perzistentní aplikační data.
Oddělení odpovědností
Jedním z architektonických principů, které dodržuji nejdůsledněji, je jasné oddělení odpovědností.
Vyhýbám se tomu, abych umisťoval:
- databázové dotazy do composables,
- API volání přímo do UI komponent,
- rozsáhlou business logiku do Activities,
- klíče perzistentního úložiště napříč celou aplikací.
Každá vrstva namísto toho dostává jasně vymezenou odpovědnost.
Projekt je díky tomu snazší pochopit a budoucí změny mají menší dopad na jeho zbytek.
Dependency Injection
Větší aplikace postavené na Jetpacku často těží z dependency injection.
ViewModel může záviset například na:
- repositories,
- službách,
- přístupu k databázi,
- API klientech.
Explicitní poskytování těchto závislostí usnadňuje testování architektury a omezuje potřebu ručně vytvářet globální objekty napříč projektem.
Tam, kde to dává smysl, mohou Android aplikace založené na Jetpacku používat pro tuto roli Hilt; současná doporučení pro Android popisují Hilt jako doporučené řešení pro dependency injection a integrují jej s Compose i ViewModel.
Testování
Dobře strukturovaná architektura Jetpacku zlepšuje testovatelnost.
Pokud UI závisí na ViewModelu a ViewModel na rozhraních repositories, lze jednotlivé komponenty testovat nezávisle.
Mohu testovat například:
- transformaci stavu,
- business logiku,
- chování databáze,
- logiku repositories
bez nutnosti spouštět kompletní uživatelské rozhraní aplikace.
To je jedna z hlavních výhod jasně vymezených architektonických hranic.
Zpracování chyb
Chyby jsou součástí aplikačního stavu.
Síťový požadavek může selhat.
Databázová operace může selhat.
Billing připojení nemusí být dostupné.
Tyto stavy reprezentuji explicitně, aby na ně UI mohlo správně reagovat.
Například:
Loading
Success(data)
Error(message)
Konkrétní stavový model se liší podle funkcionality, ale rozhraní by nemělo být nuceno hádat, zda operace proběhla úspěšně.
Kompatibilita Androidu
Jednou z původních hlavních výhod Jetpacku bylo zjednodušení podpory různých verzí Androidu.
Používám knihovny Jetpacku namísto ruční implementace kompatibilitního chování tam, kde už existuje zavedená komponenta, která daný problém řeší.
Tím se snižuje množství vlastního kódu závislého na konkrétní verzi platformy a zjednodušuje se práce s budoucími aktualizacemi Androidu.
Současný přehled Jetpacku od Googlu nadále zdůrazňuje zpětnou kompatibilitu a konzistentní chování napříč verzemi Androidu a zařízeními.
Udržování knihoven aktuálních
Vývoj Androidu se rychle vyvíjí.
Sleduji zejména:
- deprecated API,
- aktualizace knihoven,
- změny chování platformy,
- nová architektonická doporučení.
Závislosti ale neaktualizuji bezmyšlenkovitě.
Aktualizaci knihovny je stále potřeba otestovat proti skutečné aplikaci.
Stabilita je důležitější než okamžitě používat nejnovější číslo verze.
Vyhýbání se zbytečné architektuře
Jetpack poskytuje mnoho knihoven.
To ale neznamená, že každá aplikace potřebuje každou komponentu.
Jednoduchý nástroj by měl zůstat jednoduchý.
Architektonické vrstvy přidávám tehdy, když řeší skutečný problém, například:
- perzistenci,
- životní cyklus,
- navigaci,
- synchronizaci,
- testovatelnost.
Architektura má složitost snižovat, ne ji sama vytvářet.
Android Jetpack v mém technologickém stacku
Android Jetpack běžně používám společně s technologiemi, jako jsou:
- Kotlin,
- Jetpack Compose,
- Room,
- DataStore,
- Kotlin Coroutines,
- Kotlin Flow,
- SQLite,
- REST API,
- Google Play Billing,
- Google Mobile Ads / AdMob.
Jetpack poskytuje architektonický rámec, který tyto technologie propojuje do udržovatelné Android aplikace.
Proč používám Android Jetpack
Android Jetpack používám proto, že moderní Android aplikace potřebují víc než jednotlivé obrazovky a callbacky.
Potřebují předvídatelné chování životního cyklu, strukturovanou správu stavu, perzistenci, navigaci a spouštění práce na pozadí.
Jetpack poskytuje zavedené stavební bloky pro řešení těchto oblastí a umožňuje mi věnovat více vývojového úsilí samotné aplikaci.
Hodnota Jetpacku nespočívá pouze v přístupu k většímu množství Android knihoven.
Spočívá v konzistentním architektonickém základu, který pomáhá udržet aplikace srozumitelné, testovatelné a udržovatelné i s jejich dalším růstem.