onPause: co to je, ukládání stavu Activity v Androidu

Autor: IT Sectr Publikováno: 2026-03-04 Doba čtení: 10 min

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 — Activity ztrácí fokus, ale zůstává viditelné; poslední garantovaný bod ukládání dat
  • Ukládání stavu — v onPause se ukládají kritická uživatelská data: koncepty, text ve formulářích, průběh
  • Uvolňování zdrojů — kamera, mikrofon, videopřehrávač se uvolňují v onPause pro předání jiné aplikaci
  • Časový limit — onPause musí být dokončena do 100 ms; překročení způsobuje ANR a zpožďuje přechod
  • SharedPreferences.apply() — asynchronní zápis v onPause; commit() blokuje vlákno a může způsobit ANR
  • onPause vs onStop — onPause při částečné viditelnosti (dialog), onStop při úplném skrytí (jiné Activity)
  • onSaveInstanceState — volá se po onPause pro uložení dočasného stavu do Bundle

Co je onPause v Androidu

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().

onPause v Activity

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).

kotlin
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.

Ukládání stavu v onPause

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 apply()

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().

kotlin
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()
}

Room a korutiny

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.

kotlin
// 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

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.

kotlin
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.

onPause vs onStop: rozdíl a scénáře

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ářonPauseonStop
Otevření dialogového oknaVolá seNevolá se
Otevření nového Activity (neprůhledného)Volá seVolá se
Stisknutí tlačítka „Domů”Volá seVolá se
Zamknutí obrazovkyVolá seVolá se
Příchozí hovorVolá seVolá se
Průhledné Activity nad aktuálnímVolá seNevolá se
Split Screen (polovina obrazovky)Volá seNevolá se
PiP (Picture-in-Picture)Volá seNevolá 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.

Časování a výkon onPause

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.

Doporučení pro výkon

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.

kotlin
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.

Časté chyby v onPause

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í.

Synchronní zápis do databáze

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.

Registrace nových posluchačů

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.

Ignorování výjimek

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().

Ukládání nadbytečných dat

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í.

Zapomenuté super.onPause()

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

Co se stane, když zavoláte finish() v onPause?

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é.

Čím se liší onPause od onSaveInstanceState?

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.

Lze otevřít dialogové okno v onPause?

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.

Proč je onPause garantované místo ukládání a onStop ne?

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.

Jak testovat onPause v unit testech?

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í

  • onPause — Activity ztrácí vstupní fokus, ale zůstává částečně viditelné; poslední garantovaný bod ukládání dat
  • Ukládání — SharedPreferences.apply() nebo Room přes korutiny; commit() a synchronní operace jsou zakázány
  • Uvolňování zdrojů — kamera, audio fokus, videopřehrávač se uvolňují v onPause pro jinou aplikaci
  • Limit 100 ms — onPause blokuje vykreslování následujícího Activity; překročení limitu způsobuje ANR
  • onPause vs onStop — onPause při ztrátě fokusu (viditelnost zachována), onStop při úplném skrytí
  • Fragment.onPause — hierarchické volání po Activity.onPause; specifikum pro mapy a ViewPager
  • Typické chyby — synchronní zápis, registrace posluchačů, ignorování try/catch, nadbytečné ukládání
  • super.onPause() — volat co nejdříve; vynechání nezpůsobí crash, ale rozbije stavový automat

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í.

Prodiskutovat projekt

Přečtěte si také