Kotlin patří mezi hlavní programovací jazyky, které používám pro moderní vývoj aplikací pro Android a návrh aplikační architektury.
Používám ho pro produkční Android aplikace, práci s lokálními daty, asynchronní workflow, integrace API, správu stavu a aplikační logiku postavenou kolem Jetpack Compose a Android Jetpack.
Pro mě Kotlin není jen stručnější alternativou k Javě.
Jeho typový systém, null safety, coroutines a expresivní jazykové prvky z něj dělají mimořádně vhodný nástroj pro tvorbu udržovatelných Android aplikací s jasnou architekturou.
Jak používám Kotlin
Kotlin používám například pro:
- nativní vývoj Android aplikací,
- rozhraní v Jetpack Compose,
- logiku ve ViewModelech,
- coroutines,
- stav založený na Flow,
- perzistenci pomocí Room,
- DataStore,
- integrace REST API,
- Google Play Billing,
- Google Mobile Ads,
- zpracování na pozadí,
- transformaci dat,
- aplikační architekturu.
V Android projektech Kotlin obvykle tvoří centrální jazykovou vrstvu, která propojuje uživatelské rozhraní, business logiku, perzistenci a integrace s platformou.
Kotlin pro vývoj Android aplikací
Kotlin je hlavní jazyk, který používám pro nativní Android aplikace.
Typická aplikace může kombinovat:
Kotlin
↓
Android Jetpack
↓
Jetpack Compose
↓
ViewModel
↓
Repository
↓
Room / DataStore / REST API
Kotlin poskytuje jazykovou vrstvu napříč všemi těmito komponentami.
Vzniká tak konzistentní vývojové prostředí bez nutnosti přecházet mezi nesouvisejícími programovacími modely pro různé části aplikace.
Null safety
Jednou z nejdůležitějších vlastností Kotlinu je explicitní práce s null hodnotami.
Hodnota, která může chybět, musí být reprezentována jinak než hodnota, která by měla vždy existovat.
Například:
val username: String
val optionalUsername: String?
Řada potenciálních chyb souvisejících s null hodnotami se tak přesouvá přímo do typového systému.
Místo spoléhání na selhání za běhu nutí kompilátor kód explicitně počítat s chybějícími hodnotami.
To považuji za obzvlášť užitečné při práci s:
- API,
- databázemi,
- konfigurací,
- stavem životního cyklu Androidu,
- volitelným uživatelským vstupem.
Vyhýbání se nebezpečné práci s null hodnotami
Vyhýbám se nadměrnému používání non-null assertion operátoru v Kotlinu.
Kód jako:
value!!
může problém s null hodnotou jednoduše přesunout zpět do runtime.
Preferuji přístupy, jako jsou:
- safe calls,
- explicitní validace,
- výchozí hodnoty,
- early return,
- smysluplné stavové modely.
Cílem není pouze uspokojit kompilátor.
Cílem je zpřehlednit možné stavy aplikace.
Data classes
Data classes používám ve velké míře k reprezentaci strukturovaného aplikačního stavu.
Příkladem jsou:
- API modely,
- stav UI,
- databázové entity,
- konfigurace,
- doménové modely.
Data class poskytuje těmto strukturám užitečné automaticky generované chování a současně zachovává stručnou definici.
Například:
data class User(
val id: Long,
val name: String,
val active: Boolean
)
Tento přístup funguje obzvlášť dobře s architekturami založenými na immutable stavu.
Immutable stav
Kde je to praktické, preferuji neměnné stavové modely.
Místo toho, aby mnoho nesouvisejících částí aplikace měnilo stejný objekt, vytváří změna stavu novou hodnotu.
Například:
state.copy(isLoading = true)
To přirozeně zapadá do:
- Jetpack Compose,
- StateFlow,
- ViewModelu,
- jednosměrného toku dat.
Immutable stav usnadňuje pochopení i ladění změn v aplikaci.
Sealed classes a interfaces
Sealed typy používám tehdy, když má aplikace známou množinu možných stavů.
Například:
sealed interface ResultState {
data object Loading : ResultState
data class Success(val data: Data) : ResultState
data class Error(val message: String) : ResultState
}
To je užitečné pro reprezentaci:
- stavů načítání,
- výsledků API,
- navigačních událostí,
- doménově specifických stavů.
Kompilátor pak může pomoci zajistit, aby byly zpracovány všechny očekávané případy.
Extension functions
Extension functions v Kotlinu jsou užitečné pro přidávání cíleného chování bez vytváření zbytečných utility tříd.
Používám je například pro:
- konverze,
- formátování,
- mapování,
- malé doménově specifické pomocné funkce.
Extension functions udržuji blízko typům, ke kterým koncepčně patří, a nepoužívám je ke skrývání velkého množství nesouvisející logiky.
Higher-order functions
Podpora funkcí jako hodnot v Kotlinu umožňuje vytvářet expresivní a kompaktní API.
Higher-order functions používám pro:
- callbacky,
- transformace kolekcí,
- event handlery v Compose,
- konfigurovatelné chování.
Například:
items.filter { it.active }
.map { it.name }
Řadu operací se zpracováním dat tak lze vyjádřit bez zbytečně rozsáhlých smyček.
Operace s kolekcemi
Pravidelně používám kolekční API Kotlinu pro:
- filtrování,
- mapování,
- seskupování,
- řazení,
- agregaci.
Tyto operace jsou užitečné při transformaci:
- API odpovědí,
- výsledků databázových dotazů,
- UI modelů,
- konfiguračních dat.
Při práci s velkými kolekcemi ale stále věnuji pozornost výkonu.
Stručný kód je užitečný jen tehdy, pokud je zároveň vhodný pro danou zátěž.
Coroutines
Kotlin coroutines patří mezi nejdůležitější části mého workflow při vývoji Android aplikací.
Používám je pro asynchronní operace, jako jsou:
- síťové požadavky,
- přístup k databázi,
- zpracování souborů,
- billing operace,
- výpočty na pozadí.
Coroutines umožňují strukturovat asynchronní kód výrazně čistěji než hluboce vnořené callbacky.
Typický ViewModel může spouštět práci v coroutine scope respektujícím životní cyklus, zatímco UI zůstává responzivní.
Structured concurrency
Preferuji coroutine kód s jasně definovaným vlastníkem a životností.
Coroutine by měla patřit do smysluplného scope.
Například:
- práce spojená s konkrétní obrazovkou může patřit do ViewModelu,
- práce závislá na životním cyklu může patřit do jiného lifecycle-aware scope,
- perzistentní práce může místo toho vyžadovat WorkManager.
Tím se omezuje riziko, že úlohy na pozadí pokračují i poté, co komponenta, která je spustila, přestane existovat.
Dispatchers
Různé typy úloh patří do různých execution contextů.
Rozlišuji například mezi:
- prací s UI,
- síťovými operacemi,
- diskovým I/O,
- výpočetně náročným zpracováním.
Cílem je držet náročnou práci mimo hlavní UI vlákno.
Android aplikace musí zůstat responzivní i při provádění databázových nebo síťových operací.
Cancellation
Coroutines podporují kooperativní zrušení.
To je důležité v aplikacích, kde uživatel může opustit obrazovku dříve, než operace skončí.
Asynchronní práci navrhuji tak, aby zrušení nezanechalo aplikační stav nekonzistentní.
Dlouhotrvající operace by zároveň neměly zbytečně ignorovat požadavky na zrušení.
Flow
Kotlin Flow je další důležitou součástí mé Android architektury.
Používám ho tam, kde se data v čase mění a aplikace potřebuje tyto změny sledovat.
Typickými zdroji jsou:
- Room,
- DataStore,
- repository vrstvy,
- aplikační stav.
Běžná architektura vypadá například takto:
Datový zdroj
↓
Flow
↓
Repository
↓
ViewModel
↓
Jetpack Compose
Vzniká tak reaktivní datová pipeline, ve které se stav UI automaticky aktualizuje při změně podkladových dat.
StateFlow
StateFlow používám pro pozorovatelný stav, který má vždy aktuální hodnotu.
To dobře funguje ve ViewModelech, které poskytují stav obrazovky pro Compose.
Například:
val uiState: StateFlow<UiState>
UI může tento stav collectovat a odpovídajícím způsobem se vykreslovat.
Vzniká tak jasný single source of truth pro danou obrazovku.
SharedFlow
SharedFlow může být užitečný pro streamy událostí, které se nemají nutně chovat jako perzistentní stav.
Rozlišuji mezi:
- stavem,
- jednorázovými událostmi.
Tím se zabrání tomu, aby byly přechodné akce omylem reprezentovány jako trvalý stav UI.
Správný mechanismus závisí na významu konkrétní funkce.
Jetpack Compose
Kotlin a Jetpack Compose do sebe přirozeně zapadají.
UI v Compose se zapisuje přímo v Kotlinu, takže mohu používat:
- jazykové konstrukce,
- typovou bezpečnost,
- extension functions,
- stavové modely,
- coroutines
bez přechodu do samostatného XML jazyka pro UI.
Prezentační vrstva tak působí jako přirozená součást stejné aplikační architektury.
ViewModel
Kotlin používám ve ViewModelech ke správě:
- stavu UI,
- aplikačních akcí,
- asynchronní práce,
- komunikace s repository vrstvou.
ViewModel se stává hranicí mezi uživatelským rozhraním a aplikační logikou.
Composable funkce přijímají stav a callbacky místo toho, aby samy prováděly business logiku.
Room
Room je s Kotlinem úzce integrovaný.
Pro entity používám Kotlin data classes a pro přístup k databázi DAO rozhraní.
V kombinaci s coroutines a Flow může Room zpřístupňovat reaktivní databázový stav přímo do zbytku aplikační architektury.
Lokální perzistence tak zůstává předvídatelná a silně typovaná.
DataStore
DataStore přirozeně zapadá do coroutine a Flow modelu Kotlinu.
Používám ho pro:
- preference,
- nastavení,
- menší perzistentní stav.
Výsledné hodnoty mohou proudit přes repository vrstvy a ViewModely do Compose bez ručního pollingu.
REST API
Kotlin používám pro integrace REST API v Android aplikacích.
Typické odpovědnosti zahrnují:
- odesílání požadavků,
- parsování JSON,
- mapování síťových modelů,
- zpracování chyb,
- zpřístupnění doménových modelů vhodných pro aplikaci.
Tam, kde je to vhodné, odděluji struktury externího API od UI.
Formát odpovědi backendu by neměl určovat celou architekturu aplikace.
JSON
Kotlin data classes se velmi dobře hodí k mapování strukturovaných JSON dat do typovaných aplikačních objektů.
Definuji jasné modely místo předávání libovolných JSON struktur napříč aplikací.
To zlepšuje:
- čitelnost,
- automatické doplňování,
- refaktoring,
- validaci.
Aplikace pak může v případě potřeby síťová data transformovat do interních doménových modelů.
Repository architektura
Kotlin používám k implementaci repository vrstev, které koordinují různé datové zdroje.
Například:
ViewModel
↓
Repository
↙ ↘
Room REST API
nebo:
ViewModel
↓
Settings Repository
↓
DataStore
Tím zůstává UI kód nezávislý na technických detailech toho, odkud data pocházejí.
Doménové modely
U projektů, které z toho skutečně těží, preferuji oddělování důležitých doménových konceptů od struktur specifických pro konkrétní framework.
Například API odpověď, databázová entita a UI model mohou reprezentovat stejný základní koncept, ale mít rozdílné odpovědnosti.
Explicitní mapování pomáhá tyto hranice udržet jasné.
U menších aplikací se naopak vyhýbám zbytečným vrstvám tam, kde stačí jeden model.
Typová bezpečnost
Typový systém Kotlinu pomáhá explicitně definovat aplikační kontrakty.
Preferuji silné typy před volným předáváním:
- řetězců,
- celých čísel,
- map
v situacích, kdy mají data jasný doménový význam.
To může snížit chyby, jako je záměna nesouvisejících identifikátorů nebo předávání neplatného stavu mezi komponentami.
Enums
Enums používám tehdy, když má aplikace pevně definovanou množinu jednoduchých pojmenovaných hodnot.
Například:
enum class ThemeMode {
LIGHT,
DARK,
SYSTEM
}
Pro stavy, které potřebují odlišná související data nebo chování, mohou být vhodnější sealed typy.
Generics
Generics používám tam, kde komponenta skutečně potřebuje znovupoužitelné a typově bezpečné chování.
Typickými případy jsou:
- result wrappery,
- repository vrstvy,
- zpracování kolekcí,
- znovupoužitelné aplikační utility.
Nevytvářím generické abstrakce jen proto, aby kód působil sofistikovaněji.
Abstrakce by měla snižovat duplicitu nebo zlepšovat API.
Interfaces
Rozhraní jsou užitečná pro definování hranic mezi komponentami.
Mohu je používat například pro:
- repository vrstvy,
- služby,
- datové zdroje,
- testovací implementace.
Díky tomu lze jednu implementaci nahradit jinou bez nutnosti měnit všechny její uživatele.
Zlepšuje to také testování, protože reálnou závislost lze nahradit kontrolovanou testovací variantou.
Dependency Injection
Kotlin přirozeně funguje s dependency injection patterny používanými v Android aplikacích.
Místo vytváření závislostí napříč celým codebasem mohou komponenty explicitně dostávat to, co potřebují.
Může jít například o:
- repository vrstvy,
- databáze,
- API klienty,
- billing služby.
Jasně definované vlastnictví závislostí zlepšuje udržovatelnost i testování.
Interoperabilita s Javou
Kotlin má velmi dobrou interoperabilitu s Javou.
To je důležité, protože velká část Android ekosystému stále obsahuje Java kód a knihovny.
Mohu:
- volat Java knihovny z Kotlinu,
- zpřístupnit Kotlin kód Javě,
- postupně migrovat existující Java projekty.
Díky tomu je Kotlin praktický i v projektech, které ještě nejsou kompletně modernizované.
Práce s existujícím Java kódem
Migrace na Kotlin nemusí znamenat přepis celé aplikace najednou.
Novou funkcionalitu lze psát v Kotlinu, zatímco existující Java kód zůstává zachovaný.
To umožňuje postupnou modernizaci s nižším rizikem.
Postupem času lze nejrelevantnější části aplikace přesouvat do Kotlinu podle potřeby.
Android Platform API
Kotlin poskytuje přímý přístup k Android SDK.
Používám ho pro platformní funkcionalitu, jako jsou:
- oprávnění,
- lifecycle,
- intents,
- services,
- notifikace,
- systémová API.
Jetpack nabízí moderní abstrakce nad mnoha běžnými patterny, ale porozumění samotné Android platformě zůstává důležité.
Google Play Billing
Kotlin používám pro billing integrace zahrnující:
- dotazy na produkty,
- stav nákupu,
- logiku oprávnění,
- asynchronní billing operace.
Billing kód udržuji oddělený od vykreslování UI.
Zbytek aplikace pracuje se zjednodušeným stavem oprávnění místo přímé komunikace se store API.
Google Mobile Ads
Kotlin používám také při integraci reklamních SDK, jako je Google Mobile Ads.
To zahrnuje koordinaci:
- asynchronního načítání,
- lifecycle stavu,
- callbacků,
- monetizačního stavu,
- oprávnění po odstranění reklam.
Tuto logiku preferuji centralizovat místo umisťování reklamních callbacků přímo do nesouvisejících obrazovek.
Zpracování chyb
Zpracování chyb navrhuji explicitně.
Podle konkrétní vrstvy může zahrnovat:
- výjimky,
- result typy,
- sealed stavy,
- výsledky validace.
Chyby by se měly převádět do smysluplných aplikačních stavů místo toho, aby se syrová technická selhání dostávala přímo do UI.
Exceptions
Výjimky používám pro výjimečná selhání tam, kde dávají smysl, ale nepoužívám je jako náhradu běžného aplikačního stavu.
U očekávaných situací, jako jsou:
- neplatný vstup,
- nedostupná data,
- zrušení uživatelem,
může být explicitní stav často přehlednější.
Validace
Typový systém Kotlinu pomáhá se správností kódu, ale runtime data stále vyžadují validaci.
Validuji vstupy z:
- uživatelského vstupu,
- API,
- souborů,
- perzistentního úložiště.
Bezpečnost v době kompilace nedokáže zaručit, že externí data jsou platná.
Testování
Stručná syntaxe Kotlinu a explicitní architektura se dobře hodí pro testování.
Důležitou aplikační logiku navrhuji tak, aby ji bylo možné testovat nezávisle na Android UI.
Testy mohou pokrývat:
- stav ViewModelu,
- repository logiku,
- mapování,
- validaci,
- doménová pravidla.
Jasně definovaná rozhraní a hranice závislostí výrazně usnadňují přípravu testů.
Testování Android aplikací
U Android aplikací rozlišuji mezi:
- unit testy,
- integračními testy,
- UI testy.
Ne každá funkce vyžaduje všechny typy testů.
Testovací úsilí soustředím především na chování, u kterého by regrese byla nákladná nebo obtížně odhalitelná ručním testováním.
Čitelnost
Jednou z výhod Kotlinu je stručná syntaxe.
Stručný kód ale není automaticky čitelný kód.
Vyhýbám se zhušťování logiky do „chytrých“ výrazů v situacích, kdy je explicitnější implementace srozumitelnější.
Udržovatelnost je důležitější než minimální počet řádků.
Vyhýbání se nadměrnému používání jazykových funkcí
Kotlin nabízí mnoho výkonných jazykových funkcí.
Používám je záměrně.
Nadměrné používání:
- vnořených scope functions,
- extension functions,
- operator overloading,
- složitých generics
může zhoršit čitelnost kódu.
Jazyková funkce by měla zpřesňovat záměr, ne pouze dokazovat, že existuje.
Scope functions
Funkce jako:
let,apply,also,run,with
mohou vytvářet velmi expresivní kód, pokud je jejich účel jasný.
Vyhýbám se jejich dlouhému řetězení způsobem, který znesnadňuje pochopení toho, na který objekt se kód právě odkazuje.
Čitelnost zůstává prioritou.
Výkon
Kotlin je pro vývoj Android aplikací velmi vhodný, výkon ale stále závisí na způsobu zápisu kódu.
Věnuji pozornost:
- zbytečným alokacím,
- rozsáhlým transformacím kolekcí,
- blokujícím operacím,
- opakovaným výpočtům,
- nadměrným aktualizacím stavu UI.
Architektura má obvykle větší význam než mikrooptimalizace syntaxe jazyka.
Bezpečnost hlavního vlákna
Jedním z nejdůležitějších výkonnostních pravidel při vývoji Android aplikací je držet náročnou práci mimo hlavní vlákno.
Vyhýbám se spouštění:
- síťových požadavků,
- rozsáhlých databázových operací,
- zpracování souborů,
- náročných výpočtů
přímo v UI kódu.
Coroutines a vhodné provádění práce na pozadí udržují rozhraní responzivní.
Paměť
Android aplikace běží na zařízeních s omezenými prostředky.
Spotřebu paměti zohledňuji při práci s:
- velkými obrázky,
- médii,
- kolekcemi,
- daty v cache.
Silné jazykové abstrakce neodstraňují omezení samotného zařízení.
Práce na pozadí
Pro perzistentní nebo odložitelnou práci kombinuji Kotlin s Android komponentami, jako je WorkManager.
Coroutine spuštěná z ViewModelu je vhodná pro práci související s konkrétní obrazovkou.
Není ale nutně vhodná pro úlohu, která musí přežít ukončení procesu.
Mechanismus vykonání vybírám podle požadované životnosti úlohy.
Udržovatelnost
Kotlin mi pomáhá psát stručný kód, hlavním faktorem udržovatelnosti ale zůstává architektura.
Preferuji projekty s jasným oddělením mezi:
- UI,
- aplikačním stavem,
- business logikou,
- perzistencí,
- síťovou komunikací,
- integracemi s platformou.
Tím se brání tomu, aby jednotlivé obrazovky nebo ViewModely hromadily nesouvisející odpovědnosti.
Kotlin v mém technologickém stacku
Kotlin běžně používám společně s technologiemi, jako jsou:
- Android,
- Android Jetpack,
- Jetpack Compose,
- Kotlin Coroutines,
- Kotlin Flow,
- Room,
- DataStore,
- SQLite,
- REST API,
- JSON,
- Google Play Billing,
- Google Mobile Ads / AdMob,
- Git a GitHub.
Kotlin poskytuje centrální jazykovou vrstvu, která tyto technologie propojuje do jednotné Android aplikace.
Proč používám Kotlin
Kotlin používám proto, že pro vývoj Android aplikací nabízí silnou kombinaci bezpečnosti, expresivity a moderního asynchronního programování.
Jeho hodnota nespočívá jen v tom, že vyžaduje méně kódu než Java.
Null safety omezuje celou kategorii runtime chyb.
Coroutines usnadňují strukturování asynchronní logiky.
Flow podporuje reaktivní aplikační stav.
Typový systém zpřehledňuje aplikační kontrakty.
V kombinaci s Jetpack Compose a Android Jetpack mi Kotlin poskytuje jazyk a architekturu, které spolu přirozeně fungují.
Právě proto tvoří základ mého moderního Android vývojového stacku.