onPause — je metoda životního cyklu Android, která je volána, když Activity ztrácí vstupní fokus, ale zůstává částečně viditelná na obrazovce. Systém volá onPause předtím, než nové Activity vystoupí do popředí, při otevření dialogového okna, při stisknutí tlačítka „Nedávné aplikace” nebo při příchozím hovoru. Tato metoda — poslední garantovaný bod pro ukládání uživatelských dat, protože po onStop a onDestroy může systém ukončit proces bez dalších volání. Uvnitř onPause vývojář ukládá koncepty, pozastavuje animace, uvolňuje kameru a zapisuje aktuální stav UI do SharedPreferences. Více o úplném životním cyklu Activity si přečtěte v článku Activity Lifecycle.
Hlavní body
onPause — čtvrtá metoda životního cyklu Activity, která je volána, když obrazovka ztrácí vstupní fokus, ale zůstává částečně viditelná pro uživatele. Toto je „přechodový” stav mezi aktivní prací aplikace a jejím skrytím. Systém volá onPause v následujících scénářích: otevření jiného Activity (nová obrazovka překrývá aktuální), objevení dialogového okna (Dialog, PopupWindow, Snackbar nevolají onPause, ale DialogFragment ano), stisknutí tlačítka „Nedávné aplikace”, příchozí hovor, stisknutí tlačítka „Napájení” pro uzamčení obrazovky.
Hlavním úkolem onPause je připravit aplikaci na možnost, že bude skryta nebo zničena. Toto je poslední bod v životním cyklu, kde si může být vývojář jistý, že jeho kód bude proveden, než systém přejde k jiné komponentě. Po onPause systém volá onStop (pokud je Activity zcela skryto), po kterém může dojít ke zničení procesu kdykoli bez dalšího upozornění.
Podle dokumentace Android Developers (2025) by onPause měla být co nejlehčí a nejrychlejší. Dokud onPause nevrátí řízení, systém nemůže spustit další Activity — to znamená, že uživatel vidí zpoždění přechodu mezi obrazovkami. Google doporučuje dokončit onPause za méně než 100 milisekund a všechny dlouhotrvající operace (ukládání do databáze, zápis na disk) provádět asynchronně pomocí korutin nebo apply().
V Activity je metoda onPause volána pokaždé, když obrazovka přestane být aktivní, ale může být nadále částečně zobrazována. Typický příklad: uživatel otevře aplikaci „Mapy”, stiskne „Sdílet polohu” a nad Mapami se otevře systémový dialog pro výběr aplikace. Activity Map dostává onPause, ale zůstává viditelné pod dialogem. Když je dialog zavřen, Mapy dostávají onResume bez volání onStart (obrazovka nebyla zcela skryta).
class NoteEditorActivity : AppCompatActivity() {
private var binding: ActivityNoteEditorBinding? = null
private val prefs by lazy {
getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
}
override fun onPause() {
super.onPause()
// Ukládáme koncept poznámky — asynchronně
prefs.edit()
.putString("draft_title", binding?.titleInput?.text.toString())
.putString("draft_body", binding?.bodyInput?.text.toString())
.putLong("draft_timestamp", System.currentTimeMillis())
.apply()
// Pozastavujeme video
binding?.videoPlayer?.pause()
// Uvolňujeme exkluzivní zdroje
releaseCamera()
releaseAudioFocus()
}
override fun onResume() {
super.onResume()
// Obnovujeme koncept
binding?.titleInput?.setText(prefs.getString("draft_title", ""))
binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
acquireCamera()
acquireAudioFocus()
}
}
Příklad NoteEditorActivity demonstruje správnou práci s onPause: uložení konceptu do SharedPreferences pomocí apply(), pozastavení video souboru, uvolnění kamery a audio fokusu. Každé volání — lehké a rychlé, neblokující UI vlákno dostatečně pro ANR. Všimněte si pořadí: super.onPause() je voláno na prvním řádku — to zaručuje, že systémová logika bude provedena i při výjimce v uživatelském kódu.
onPause — poslední bod, kde může vývojář garantovaně uložit uživatelská data před tím, než bude aplikace skryta nebo ukončena systémem. Po onStop může systém zničit proces při nedostatku paměti bez volání onDestroy. Metoda onSaveInstanceState() je volána po onPause, ale její Bundle není určen pro dlouhodobé ukládání — žije pouze do dalšího onCreate.
SharedPreferences s asynchronním apply() — optimální způsob ukládání malých objemů dat v onPause. Na rozdíl od commit(), který synchronně zapisuje data na disk a vrací boolean, apply() okamžitě ukládá data do paměti a plánuje asynchronní zápis na disk. To trvá méně než 1 milisekundu v UI vlákně oproti 10–100 milisekundám u commit().
override fun onPause() {
super.onPause()
// ❌ Špatně: synchronní zápis blokuje vlákno
// prefs.edit().putInt("score", score).commit()
// ✅ Dobře: asynchronní zápis
prefs.edit().putInt("score", score).apply()
// Pro složité objekty — ukládání do mezipaměti ve ViewModel
viewModel.saveState()
}
Pro strukturovaná data (SQLite přes Room) se v onPause používají korutiny s lifecycleScope. ViewModelScope automaticky ruší korutinu při zničení ViewModel, což zabraňuje zápisu do uzavřené databáze. Zápis přes Room s korutinami trvá 5–15 milisekund a neblokuje UI vlákno.
// Ve ViewModel:
fun saveDraft(title: String, body: String) {
viewModelScope.launch(Dispatchers.IO) {
noteDao.insert(NoteDraft(title = title, body = body))
}
}
// V Activity.onPause:
viewModel.saveDraft(
binding?.titleInput?.text.toString(),
binding?.bodyInput?.text.toString()
)
onPause ve Fragmentu je volána, když Fragment přestane být aktivní, ale může zůstat viditelný. K tomu dochází, když: Fragment je nahrazen jiným Fragmentem pomocí FragmentTransaction; Fragment přestane být aktuální stránkou ve ViewPager; Activity obsahující Fragment dostává onPause. Interakce mezi onPause Activity a onPause Fragmentu je přísně hierarchická: nejprve onPause dostává Activity, poté všechny jeho Fragmenty.
class MapFragment : Fragment() {
private var mapController: MapController? = null
override fun onPause() {
super.onPause()
mapController?.stopFollowMode()
binding?.mapContainer?.alpha = 0.7f
}
override fun onResume() {
super.onResume()
binding?.mapContainer?.alpha = 1.0f
if (isVisible) {
mapController?.startFollowMode()
}
}
}
Specifika práce s mapami v onPause: Google Maps a Yandex Maps spotřebovávají značné GPU zdroje v aktivním režimu sledování (follow mode). Při ztrátě fokusu má smysl vypnout animaci mapy a snížit frekvenci aktualizace značek a při návratu fokusu obnovit plnou funkcionalitu. To zlepšuje výkon a snižuje spotřebu energie při přepínání mezi obrazovkami.
Jedním z nejčastějších zdrojů zmatení u začínajících Android vývojářů — nepochopení rozdílu mezi onPause a onStop. Podívejme se na každý scénář a určeme správnou metodu.
| Scénář | onPause | onStop |
|---|---|---|
| Otevření dialogového okna | Volá se | Nevolá se |
| Otevření nového Activity (neprůhledného) | Volá se | Volá se |
| Stisknutí tlačítka „Domů” | Volá se | Volá se |
| Zamknutí obrazovky | Volá se | Volá se |
| Příchozí hovor | Volá se | Volá se |
| Průhledné Activity nad aktuálním | Volá se | Nevolá se |
| Split Screen (polovina obrazovky) | Volá se | Nevolá se |
| PiP (Picture-in-Picture) | Volá se | Nevolá se |
Hlavní pravidlo: onPause je volána při každé ztrátě fokusu, onStop — pouze při úplné ztrátě viditelnosti. Pokud Activity zůstává viditelné (i částečně), onStop není voláno. To je kritické pro režimy Split Screen, PiP a průhledná Activity — zde onPause/onResume fungují, ale onStart/onStop ne.
onPause — časově nejkritičtější metoda životního cyklu, protože blokuje vykreslování následujícího Activity. Systém čeká na dokončení onPause aktuálního Activity, než zobrazí nové. Pokud onPause trvá déle než 100 milisekund, uživatel zaznamená zpoždění přechodu; pokud déle než 5 sekund — systém zobrazí ANR.
Průvodce výkonem Android Google (2025) poskytuje následující doporučení pro onPause: neprovádějte síťové požadavky — měly by být zrušeny nebo přesunuty do WorkManager; nezapisujte velké soubory na disk — použijte BufferedWriter v běžném vlákně; neprovádějte složité SQL dotazy — operace Room by měly být asynchronní pomocí korutin; vyhněte se vytváření nových objektů — garbage collection v onPause zhoršuje zpoždění; používejte apply() místo commit() pro SharedPreferences.
override fun onPause() {
super.onPause()
// ❌ Špatně: HTTP požadavek blokuje UI
// val response = api.syncSave(data).execute()
// ❌ Špatně: synchronní zápis do souboru
// FileOutputStream(file).write(data)
// ✅ Dobře: asynchronní ukládání
lifecycleScope.launch {
withContext(Dispatchers.IO) {
api.saveData(data)
fileDao.write(data)
}
}
// ✅ Dobře: lehký zápis do SharedPreferences
prefs.edit().putString("key", value).apply()
}
Profilování onPause pomocí Android Studio Profiler (graf CPU) ukazuje přesný čas provedení. Pokud onPause trvá více než 100 ms, Profiler zvýrazní metodu žlutě, a více než 500 ms — červeně. V komerčních projektech IT Sectr používáme Macrobenchmark testy, které automaticky kontrolují čas přechodu mezi Activity a signalizují regresi výkonu v CI pipeline.
I zkušení vývojáři dělají chyby v onPause. Podívejme se na pět typických problémů a jejich řešení.
Volání Room DAO se synchronním dotazem (.executeAsObservable() bez korutin) v onPause blokuje UI vlákno na 10–50 ms. Pokud v té době dojde k GC nebo konkurenci o zápis do databáze, zpoždění může dosáhnout 200–500 ms. Řešení: používejte korutiny s Dispatchers.IO nebo apply() pro SharedPreferences.
onPause není místo pro registraci posluchačů. Pokud zaregistrujete BroadcastReceiver v onPause, zůstane aktivní, když Activity již není viditelné. Registrace by měla být pouze v onStart/onResume a v onPause/onStop — pouze odhlášení. Výjimka — Intent-driven API, která vyžadují registraci před voláním.
Pokud v onPause dojde k neošetřené výjimce, systém nevolá onStop a onDestroy. Activity zamrzne v neurčitém stavu a onResume při návratu nemusí správně obnovit uvolněné zdroje. Řešení: obalte kritické operace do try/catch s logováním pomocí Log.e().
Není třeba ukládat v onPause data, která lze snadno obnovit. Například výsledky API požadavků se ukládají do cache v Room nebo DataStore v okamžiku přijetí, ne v onPause. Ukládejte pouze to, co uživatel zadal ručně a nelze automaticky obnovit — text v polích, vybrané prvky, pozici posouvání.
super.onPause() by mělo být voláno, ale na rozdíl od onCreate jeho absence nezpůsobuje okamžitý crash. Systém „promíjí” vynechání super v onPause, ale interní stavový automat přejde do nesprávného stavu. Další volání onResume nemusí obnovit vstupní fokus a Activity zůstane „zamrzlé”. Vždy volejte super.onPause() co nejdříve.
Často kladené otázky
Volání finish() v onPause ukončí Activity ihned po návratu z metody. Toto je správný scénář, pokud při ztrátě fokusu potřebujete zavřít obrazovku (například obrazovku autorizace při minimalizaci aplikace). Nicméně finish() spouští celý cyklus ukončení: onStop → onDestroy, což přidává zpoždění přechodu. Používejte finish() v onPause pouze když je to skutečně nutné.
onPause — pro ukládání dat, která musí přežít ukončení procesu (koncepty v SharedPreferences/Room). onSaveInstanceState — pro ukládání dočasného stavu UI, který je potřeba pouze do dalšího onCreate (pozice posouvání, vybraná záložka). Bundle onSaveInstanceState se neukládá při úplném ukončení aplikace — existuje pouze v paměti. Data onPause se ukládají na disk a přežijí restart.
Nedoporučuje se. Otevření dialogu nebo vyskakovacího okna v onPause vede k WindowLeakException, pokud je Activity již ukončeno. Pokud potřebujete zobrazit oznámení při ztrátě fokusu, použijte NotificationManager (systémová oznámení) — je to bezpečné a uživatelem očekávané. Pro odložené akce použijte AlarmManager nebo WorkManager.
onPause je garantovaně volána předtím, než Activity přestane být aktivní. onStop nemusí být voláno, pokud systém zabije proces pro uvolnění paměti — v tomto případě není volán ani onDestroy. onPause je jediná metoda po onResume, která je volána vždy, bez ohledu na důvod ztráty fokusu. Proto se všechna kritická data ukládají právě v onPause.
Pro testování onPause se používá Robolectric nebo FragmentScenario z AndroidX Test. FragmentScenario.create() → moveToState(State.STARTED) → moveToState(State.RESUMED) → moveToState(State.STARTED) sekvenčně volá onPause. Poté se ověřuje, že data byla uložena v SharedPreferences nebo že kamera byla uvolněna pomocí mock objektu. Robolectric 4.12+ podporuje emulaci onPause/onResume bez fyzického zařízení.
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é