Not Running — počáteční stav životního cyklu mobilní aplikace, ve kterém aplikace ještě nebyla spuštěna nebo již ukončila činnost. Zjistěte, jak systém iOS a Android spravuje tento stav, jaké události vedou k přechodu z Not Running a jak správně zpracovat spuštění a ukončení aplikace v Swift a Kotlin.
Hlavní body
Not Running — je základní stav životního cyklu mobilní aplikace, ve kterém aplikace není načtena do RAM zařízení a nespotřebovává systémové prostředky. V iOS a Android tento stav znamená úplnou absenci procesů a vláken spojených s aplikací. Uživatel vidí ikonu aplikace na ploše, ale samotná aplikace není aktivní a nenachází se v seznamu nedávných.
Když uživatel klepne na ikonu, systém vytvoří nový proces, načte spustitelný kód do paměti a inicializuje všechny potřebné datové struktury. Tento proces se nazývá studený start (cold start) a je nejnáročnější z hlediska doby načítání.
Systém může přesunout aplikaci do Not Running z jakéhokoli jiného stavu. Pokud je aplikace na pozadí (Background) nebo pozastavena (Suspended), operační systém má právo ji uvolnit při nedostatku RAM pro prioritnější úkoly — například pro aktivní aplikaci v popředí.
Vývojář musí mít na paměti, že aplikace může být systémem ukončena kdykoli, když je na pozadí. To znamená, že všechna neuložená data mohou být ztracena. Proto je kriticky důležité ukládat stav do úložišť klíč-hodnota (UserDefaults, SharedPreferences) nebo do lokální databáze při přechodech z Active do Background.
iOS používá priority na základě aktuálního stavu aplikace: Active má nejvyšší prioritu, poté Inactive, Background, Suspended a nakonec Not Running — minimální prioritu. Android používá podobnou hierarchii procesů: proces Foreground má prioritu OOM_ADJ = 0, proces Visible = 100, proces Service = 200, proces Background = 300, proces Empty = 400. Čím vyšší hodnota, tím větší pravděpodobnost, že proces bude ukončen při nedostatku paměti.
| Platforma | Stav | Priorita uvolnění | Popis |
|---|---|---|---|
| iOS | Not Running | Nejvyšší | Aplikace není načtena — systémový prostředek nespotřebováván |
| iOS | Suspended | Vysoká | Aplikace v paměti, ale kód není vykonáván — první cíl pro uvolnění |
| iOS | Background | Střední | Aplikace vykonává úlohu na pozadí — uvolněna po vypršení časového limitu |
| iOS | Active | Nízká | Aktivní aplikace — uvolněna pouze při kritickém nedostatku paměti |
| Android | Empty Process | Nejvyšší | Proces bez aktivních komponent — odstraněn první |
| Android | Background Process | Vysoká | Proces na pozadí bez viditelného Activity |
| Android | Foreground Service | Nízká | Služba s oznámením — zřídka ukončena |
| Android | Foreground Process | Minimální | Aktivní Activity — ukončeno jako poslední |
Studený start (cold start) nastává, když aplikace přechází z Not Running přímo do Active. Systém vytvoří nový proces, načte třídy, inicializuje statická pole, vytvoří hlavní vlákno a spustí UI framework. V iOS to znamená volání application(_:didFinishLaunchingWithOptions:), v Android — volání Application.onCreate() a Activity.onCreate(). Doba studeného startu se může pohybovat od 200 ms do několika sekund v závislosti na složitosti aplikace.
Teplý start (warm start nebo hot start) — aplikace byla ve stavu Suspended a vrací se do provozu bez úplného znovunačtení. Systém obnoví poslední zásobník UI z paměti a uživatel pokračuje v práci na stejném místě. Teplý start je výrazně rychlejší než studený, protože většina kódu je již načtena v paměti. V iOS teplý start nevolá application(_:didFinishLaunchingWithOptions:), pouze applicationWillEnterForeground a applicationDidBecomeActive.
Rozdíl mezi studeným a teplým startem je kritický pro uživatelský zážitek. Při studeném startu musí vývojář zajistit, aby spuštění proběhlo co nejrychleji — líná inicializace modulů, odložené načítání těžkých zdrojů, minimalizace práce v hlavním vlákně při startu. Google doporučuje studený start ne delší než 500 ms, Apple — ne delší než 400 ms pro iOS.
// Měření doby studeného startu v Android
class App : Application() {
private var startTime: Long = 0L
override fun onCreate() {
super.onCreate()
startTime = System.currentTimeMillis()
}
fun getStartupTime(): Long {
return System.currentTimeMillis() - startTime
}
}
// Spuštění Activity s línou inicializací
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by lazy {
ViewModelProvider(this).get(MainViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Pouze nezbytné minimum pro první snímek
setupNavigation()
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// Těžká inicializace po vykreslení
initializeHeavyModules()
}
}Příklad ukazuje měření doby studeného startu v Android. Application.onCreate() je volán při přechodu z Not Running do Active. Časové razítko je zaznamenáno při startu procesu. Activity používá línou inicializaci přes lazy-delegát, aby neblokoval první snímek. onPostCreate je optimální místo pro inicializaci těžkých modulů, protože UI je již vykresleno.
V iOS je Not Running spravováno přes delegáta UIApplicationDelegate. Klíčové metody: application(_:didFinishLaunchingWithOptions:) je volán po studeném startu, applicationWillTerminate(_:) je volán před ukončením aplikace uživatelem. Systém může ukončit aplikaci bez volání applicationWillTerminate — například při nouzovém ukončení nebo uvolnění paměti. iOS nezaručuje volání této metody, proto by data měla být ukládána v applicationDidEnterBackground.
Uživatel může ručně ukončit aplikaci přejetím v App Switcher. Systém může uvolnit aplikaci z paměti na pozadí. Aplikace může nouzově skončit (crash). Ve všech případech jsou všechny objekty vytvořené během spuštění zničeny. Stav, který nebyl uložen, je nenávratně ztracen. V iOS 13+ pro ukládání stavu se doporučuje používat NSUserActivity nebo mechanismus state restoration přes UIApplication.stateRestorationIdentifier.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Studený start: aplikace přešla z Not Running
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Inicializace minimální sady služeb
setupAnalytics()
configureAppearance()
return true
}
// Aplikace ukončuje činnost — pouze ruční zavření
func applicationWillTerminate(
_ application: UIApplication
) {
saveCriticalData()
}
// Ukládání dat před přechodem na pozadí
func applicationDidEnterBackground(
_ application: UIApplication
) {
saveApplicationState()
}
private func saveCriticalData() {
UserDefaults.standard.synchronize()
}
private func saveApplicationState() {
let state = ["lastScreen": "main", "timestamp": Date()]
try? NSKeyedArchiver.archivedData(
withRootObject: state,
requiringSecureCoding: true
)
}
}Kód ukazuje správné zpracování Not Running v iOS. applicationWillTerminate je volán pouze při ručním ukončení uživatelem. Ukládání kritických dat je duplikováno v applicationDidEnterBackground, protože tato metoda je zaručeně volána před přechodem na pozadí. State restoration umožňuje uložit zásobník UI pro pozdější obnovení při studeném startu.
V Android Not Running znamená, že proces aplikace neexistuje. Linuxový systém, na kterém je Android založen, spravuje procesy prostřednictvím mechanismu Zygote. Při spuštění aplikace Zygote forknout nový proces, načte Dalvik/ART a zavolá Application.onCreate(). V Android neexistuje přímý analog applicationWillTerminate — systém může proces kdykoli ukončit bez varování.
Když je Activity poprvé voláno, systém vytvoří proces, Application a Activity prostřednictvím řetězce onCreate → onStart → onResume. Pokud uživatel stiskne Back, Activity je zničeno (onDestroy) a proces může být systémem ukončen. Klíčový rozdíl oproti iOS: v Android může proces nadále existovat i bez aktivních Activity — například pokud běží Foreground Service nebo je aktivní BroadcastReceiver.
// Zpracování Not Running přes SavedStateHandle ve ViewModel
class MainViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
companion object {
private const val KEY_LAST_SCREEN = "last_screen"
private const val KEY_USER_DATA = "user_data"
}
fun saveCurrentState(screen: String, data: String) {
savedStateHandle[KEY_LAST_SCREEN] = screen
savedStateHandle[KEY_USER_DATA] = data
}
fun restoreState(): AppState? {
val screen = savedStateHandle.get<String>(KEY_LAST_SCREEN)
val data = savedStateHandle.get<String>(KEY_USER_DATA)
return if (screen != null && data != null) {
AppState(screen, data)
} else null
}
}
// Application — první zpětné volání po Not Running
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
initCrashReporter()
initDependencyInjection()
}
}SavedStateHandle — komponenta Android Architecture Components, která automaticky ukládá stav při přechodu do Not Running a obnovuje jej při studeném startu. ViewModel vytvořený přes ViewModelProvider přežije otočení obrazovky a zničení Activity. Při ukončení procesu jsou data z SavedStateHandle serializována do Bundle a uložena v saved instance state.
Not Running nastává z několika důvodů. Uživatel ručně zavře aplikaci. Systém uvolní aplikaci při nedostatku paměti. Aplikace nouzově skončí s výjimkou. V Android může systém ukončit proces při hromadné aktualizaci aplikací nebo restartu zařízení. iOS může ukončit aplikaci při vypršení časového limitu úlohy na pozadí (obvykle 30 sekund).
| Důvod | iOS | Android | Možnost prevence |
|---|---|---|---|
| Ruční zavření uživatelem | Přejetí v App Switcher | Přejetí z Recents | Ne — akce uživatele |
| Nedostatek paměti | Aktivace memory warning | onTrimMemory / LMK | Částečně — optimalizace paměti |
| Crash aplikace | NSException / signál | UncaughtException / ANR | Ano — zpracování chyb a crash-reporting |
| Časový limit úlohy na pozadí | 30 s pro Background task | 10 min pro JobScheduler | Ano — správné plánování úloh |
| Restart OS | Volání applicationWillTerminate | Broadcast ACTION_SHUTDOWN | Ne — systémová událost |
| Aktualizace aplikace | Nenastává (iOS Sandbox) | Proces končí při aktualizaci APK | Ne — systémová aktualizace |
Pro iOS použijte konzolové logování v applicationWillTerminate a applicationDidFinishLaunching. Přidejte příznak do UserDefaults při každém spuštění — pokud při příštím startu příznak chybí, aplikace byla ukončena nesprávně. V Android použijte ActivityManager.isBackgroundRestricted() pro kontrolu, zda aplikace může spouštět úlohy na pozadí. Sledujte také onTrimMemory(TRIM_MEMORY_COMPLETE) — to je signál, že proces bude ukončen.
První pravidlo — nikdy nepředpokládejte, že applicationWillTerminate nebo onDestroy budou volány. Ukládejte kriticky důležitá data při každém přechodu z Active do Background. Používejte úložiště klíč-hodnota pro jednoduchá nastavení a SQLite/Room pro strukturovaná data.
Druhé pravidlo — měřte dobu studeného startu a optimalizujte ji. Líná inicializace, minimalizace práce v hlavním vlákně, předběžné načítání zdrojů, použití SplashScreen API — to vše zlepšuje vnímání doby spuštění. Google doporučuje studený start pod 200 ms pro vynikající UX.
Třetí pravidlo — implementujte State Restoration. V iOS používejte UIApplication.stateRestorationIdentifier a NSUserActivity. V Android používejte SavedStateHandle ve ViewModel v kombinaci s onSaveInstanceState. To uživateli umožní pokračovat v práci na stejném místě po restartu aplikace.
Čtvrté pravidlo — zpracovávejte launchOptions a Intent, se kterými byla aplikace spuštěna po Not Running. Deep linky, push oznámení, univerzální odkazy — všechny jsou předávány prostřednictvím parametrů spuštění. Vývojář musí tato data správně extrahovat a nasměrovat uživatele na odpovídající obrazovku.
// Zpracování deep link po studeném startu
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Kontrola, zda přišlo oznámení
if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
handleNotification(notification)
}
// Kontrola deep link
if let url = launchOptions?[.url] as? URL {
handleDeepLink(url)
}
return true
}
private func handleDeepLink(_ url: URL) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let screenId = components.queryItems?.first(where: { $0.name == "screen" })?.value
else { return }
openScreen(screenId)
}Kód ukazuje zpracování parametrů spuštění při studeném startu v iOS. launchOptions obsahuje data, se kterými systém aplikaci spustil. Oznámení, deep linky a univerzální odkazy jsou předávány prostřednictvím tohoto slovníku. Vývojář musí správně zpracovat všechny možné scénáře spuštění, aby zajistil bezproblémový uživatelský zážitek.
Často kladené otázky
Data, která byla uložena v trvalém úložišti (UserDefaults, Core Data, SharedPreferences, Room), jsou zachována. Data v RAM — proměnné, mezipaměť, stav ViewModel bez SavedStateHandle — jsou nenávratně ztracena. Proto je kriticky důležité ukládat stav aplikace při každém přechodu na pozadí.
Při studeném startu se volá application(_:didFinishLaunchingWithOptions:). Při teplém startu (návrat z Suspended) se tato metoda nevolá — aktivují se pouze applicationWillEnterForeground a applicationDidBecomeActive. Pokud potřebujete provést akci pouze při studeném startu, nastavte příznak v didFinishLaunchingWithOptions.
Ano. Foreground Service s trvalým oznámením zabraňuje ukončení procesu systémem, i když jsou všechna Activity zničena. Background Service (startService bez foreground) může být systémem kdykoli zastaven. Běžící Service znamená, že proces existuje a už se nejedná o Not Running.
Na iOS simulátoru ukončete aplikaci přes App Switcher (Cmd+Shift+H dvakrát, přejeďte nahoru). Na Android emulátoru použijte adb shell am force-stop com.example.app nebo tlačítko Stop v Logcat. Poté aplikaci znovu spusťte — to bude čistý studený start z Not Running.
Kill-switch — serverový příkaz pro nouzové ukončení aplikace. Používá se v bankovních a podnikových aplikacích pro vzdálené blokování přístupu. Pokud aplikace obdržela kill příkaz, při příštím studeném startu zablokuje UI a vyžádá opětovnou autorizaci. V iOS je kill-switch implementován prostřednictvím remote notifications s příznakem blokování.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také