onPause — to metoda cyklu życia Android, która jest wywoływana, gdy Activity traci fokus wprowadzania, ale pozostaje częściowo widoczne na ekranie. System wywołuje onPause zanim nowe Activity pojawi się na pierwszym planie, przy otwarciu okna dialogowego, po naciśnięciu przycisku „Ostatnie aplikacje” lub podczas połączenia przychodzącego. Ta metoda to ostatni gwarantowany punkt do zapisania danych użytkownika, ponieważ po onStop i onDestroy system może zakończyć proces bez dodatkowych wywołań. Wewnątrz onPause programista zapisuje szkice, wstrzymuje animacje, zwalnia kamerę i zapisuje bieżący stan UI w SharedPreferences. Więcej o pełnym cyklu życia Activity przeczytasz w artykule Activity Lifecycle.
Najważniejsze
onPause — czwarta metoda cyklu życia Activity, która jest wywoływana, gdy ekran traci fokus wprowadzania, ale pozostaje częściowo widoczny dla użytkownika. Jest to stan „przejściowy” między aktywną pracą aplikacji a jej ukryciem. System wywołuje onPause w następujących scenariuszach: otwarcie innego Activity (nowy ekran zasłania bieżący), pojawienie się okna dialogowego (Dialog, PopupWindow, Snackbar nie wywołują onPause, a DialogFragment — wywołuje), naciśnięcie przycisku „Ostatnie aplikacje”, połączenie przychodzące, naciśnięcie przycisku „Zasilanie” do zablokowania ekranu.
Głównym zadaniem onPause jest przygotowanie aplikacji na to, że może zostać ukryta lub zniszczona. To ostatni punkt w cyklu życia, gdzie programista może być pewien, że jego kod zostanie wykonany, zanim system przejdzie do innego komponentu. Po onPause system wywołuje onStop (jeśli Activity jest całkowicie ukrywane), po którym zniszczenie procesu może nastąpić w dowolnym momencie bez dodatkowych powiadomień.
Zgodnie z dokumentacją Android Developers (2025), onPause powinien być maksymalnie lekki i szybki. Dopóki onPause nie zwróci sterowania, system nie może uruchomić następnego Activity — oznacza to, że użytkownik widzi opóźnienie przejścia między ekranami. Google zaleca zakończenie onPause w mniej niż 100 milisekund, a wszystkie długotrwałe operacje (zapis do bazy danych, zapis na dysk) wykonywać asynchronicznie przez korutyny lub apply().
W Activity metoda onPause jest wywoływana za każdym razem, gdy ekran przestaje być aktywny, ale może nadal być częściowo wyświetlany. Typowy przykład: użytkownik otwiera aplikację „Mapy”, naciska „Udostępnij lokalizację”, a nad Mapami otwiera się systemowy dialog wyboru aplikacji. Activity Map otrzymuje onPause, ale pozostaje widoczne pod dialogiem. Gdy dialog zostanie zamknięty, Mapy otrzymują onResume bez wywołania onStart (ekran nie został całkowicie ukryty).
class NoteEditorActivity : AppCompatActivity() {
private var binding: ActivityNoteEditorBinding? = null
private val prefs by lazy {
getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
}
override fun onPause() {
super.onPause()
// Zapisujemy szkic notatki — asynchronicznie
prefs.edit()
.putString("draft_title", binding?.titleInput?.text.toString())
.putString("draft_body", binding?.bodyInput?.text.toString())
.putLong("draft_timestamp", System.currentTimeMillis())
.apply()
// Wstrzymujemy wideo
binding?.videoPlayer?.pause()
// Zwalniamy ekskluzywne zasoby
releaseCamera()
releaseAudioFocus()
}
override fun onResume() {
super.onResume()
// Przywracamy szkic
binding?.titleInput?.setText(prefs.getString("draft_title", ""))
binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
acquireCamera()
acquireAudioFocus()
}
}
Przykład NoteEditorActivity demonstruje prawidłową pracę z onPause: zapis szkicu w SharedPreferences przez apply(), wstrzymanie pliku wideo, zwolnienie kamery i fokusa audio. Każde wywołanie jest lekkie i szybkie, nie blokujące wątku UI na czas wystarczający do ANR. Zwróć uwagę na kolejność: super.onPause() jest wywoływane w pierwszej linii — gwarantuje to wykonanie logiki systemowej nawet przy wyjątku w kodzie użytkownika.
onPause — ostatni punkt, gdzie programista może gwarantowanie zapisać dane użytkownika przed ukryciem lub zabiciem aplikacji przez system. Po onStop system może zniszczyć proces przy braku pamięci bez wywołania onDestroy. Metoda onSaveInstanceState() jest wywoływana po onPause, ale jej Bundle nie jest przeznaczony do długotrwałego przechowywania — żyje tylko do następnego onCreate.
SharedPreferences z asynchronicznym apply() — optymalny sposób zapisu niewielkich ilości danych w onPause. W przeciwieństwie do commit(), który synchronicznie zapisuje dane na dysk i zwraca boolean, apply() natychmiast zapisuje dane w pamięci i planuje asynchroniczny zapis na dysk. Zajmuje to mniej niż 1 milisekundę w wątku UI wobec 10–100 milisekund u commit().
override fun onPause() {
super.onPause()
// ❌ Źle: synchroniczny zapis blokuje wątek
// prefs.edit().putInt("score", score).commit()
// ✅ Dobrze: asynchroniczny zapis
prefs.edit().putInt("score", score).apply()
// Dla złożonych obiektów — buforowanie w ViewModel
viewModel.saveState()
}
Do danych strukturalnych (SQLite przez Room) w onPause używa się korutyn z lifecycleScope. ViewModelScope automatycznie anuluje korutynę przy zniszczeniu ViewModel, co zapobiega zapisowi do zamkniętej bazy danych. Zapis przez Room z korutynami zajmuje 5–15 milisekund i nie blokuje wątku UI.
// W ViewModel:
fun saveDraft(title: String, body: String) {
viewModelScope.launch(Dispatchers.IO) {
noteDao.insert(NoteDraft(title = title, body = body))
}
}
// W Activity.onPause:
viewModel.saveDraft(
binding?.titleInput?.text.toString(),
binding?.bodyInput?.text.toString()
)
onPause we Fragment jest wywoływany, gdy Fragment przestaje być aktywny, ale może pozostać widoczny. Dzieje się tak, gdy: Fragment jest zastępowany innym Fragment przez FragmentTransaction; Fragment przestaje być bieżącą stroną w ViewPager; Activity zawierające Fragment otrzymuje onPause. Interakcja między onPause Activity a onPause Fragment jest ściśle hierarchiczna: najpierw onPause otrzymuje Activity, następnie wszystkie jego 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()
}
}
}
Specyfika pracy z mapami w onPause: Google Maps i Yandex Maps zużywają znaczne zasoby GPU w aktywnym trybie śledzenia (follow mode). Przy utracie fokusu warto wyłączyć animację mapy i zmniejszyć częstotliwość odświeżania markerów, a przy powrocie fokusu — przywrócić pełną funkcjonalność. Poprawia to wydajność i zmniejsza zużycie energii przy przełączaniu między ekranami.
Jednym z najczęstszych źródeł nieporozumień u początkujących programistów Android jest brak zrozumienia różnicy między onPause a onStop. Rozważmy każdy scenariusz i określmy właściwą metodę.
| Scenariusz | onPause | onStop |
|---|---|---|
| Otwarcie okna dialogowego | Wywoływane | Nie wywoływane |
| Otwarcie nowego Activity (nie przezroczystego) | Wywoływane | Wywoływane |
| Naciśnięcie przycisku „Home” | Wywoływane | Wywoływane |
| Blokada ekranu | Wywoływane | Wywoływane |
| Połączenie przychodzące | Wywoływane | Wywoływane |
| Przezroczyste Activity nad bieżącym | Wywoływane | Nie wywoływane |
| Split Screen (połowa ekranu) | Wywoływane | Nie wywoływane |
| PiP (Picture-in-Picture) | Wywoływane | Nie wywoływane |
Główna zasada: onPause jest wywoływane przy każdej utracie fokusu, onStop — tylko przy całkowitej utracie widoczności. Jeśli Activity pozostaje widoczne (nawet częściowo), onStop nie jest wywoływane. Jest to krytyczne dla trybów Split Screen, PiP i przezroczystych Activity — tutaj onPause/onResume działają, a onStart/onStop — nie.
onPause — najbardziej krytyczna czasowo metoda cyklu życia, ponieważ blokuje renderowanie następnego Activity. System czeka na zakończenie onPause bieżącego Activity, zanim pokaże nowe. Jeśli onPause wykonuje się dłużej niż 100 milisekund, użytkownik zauważa opóźnienie przejścia; jeśli dłużej niż 5 sekund — system pokazuje ANR.
Google Android Performance Guide (2025) podaje następujące zalecenia dla onPause: nie wykonuj żądań sieciowych — powinny być anulowane lub przeniesione do WorkManager; nie zapisuj dużych plików na dysk — używaj BufferedWriter w wątku tła; nie wykonuj złożonych zapytań SQL — operacje Room powinny być asynchroniczne przez korutyny; unikaj tworzenia nowych obiektów — garbage collection w onPause pogłębia opóźnienie; używaj apply() zamiast commit() dla SharedPreferences.
override fun onPause() {
super.onPause()
// ❌ Źle: żądanie HTTP blokuje UI
// val response = api.syncSave(data).execute()
// ❌ Źle: synchroniczny zapis do pliku
// FileOutputStream(file).write(data)
// ✅ Dobrze: asynchroniczne zapisywanie
lifecycleScope.launch {
withContext(Dispatchers.IO) {
api.saveData(data)
fileDao.write(data)
}
}
// ✅ Dobrze: lekki zapis do SharedPreferences
prefs.edit().putString("key", value).apply()
}
Profilowanie onPause przez Android Studio Profiler (wykres CPU) pokazuje dokładny czas wykonania. Jeśli onPause zajmuje ponad 100 ms, Profiler podświetla metodę na żółto, a ponad 500 ms — na czerwono. W komercyjnych projektach IT Sectr używamy testów Macrobenchmark, które automatycznie sprawdzają czas przejścia między Activity i sygnalizują regresję wydajności w pipeline CI.
Doświadczeni programiści również popełniają błędy w onPause. Rozważmy pięć typowych problemów i ich rozwiązania.
Wywołanie Room DAO z synchronicznym zapytaniem (.executeAsObservable() bez korutyn) w onPause blokuje wątek UI na 10–50 ms. Jeśli w tym momencie występuje GC lub konkurencja o zapis do bazy, opóźnienie może osiągnąć 200–500 ms. Rozwiązanie: używaj korutyn z Dispatchers.IO lub apply() dla SharedPreferences.
onPause nie jest miejscem do rejestracji słuchaczy. Jeśli zarejestrujesz BroadcastReceiver w onPause, pozostanie aktywny, gdy Activity nie będzie już widoczne. Rejestracja powinna być tylko w onStart/onResume, a w onPause/onStop — tylko wyrejestrowanie. Wyjątkiem są Intent-driven API, które wymagają rejestracji przed wywołaniem.
Jeśli w onPause wystąpi nieobsłużony wyjątek, system nie wywołuje onStop i onDestroy. Activity zawiesza się w nieokreślonym stanie, a onResume przy powrocie może nie przywrócić prawidłowo zwolnionych zasobów. Rozwiązanie: otaczaj krytyczne operacje w try/catch z logowaniem przez Log.e().
Nie trzeba zapisywać w onPause danych, które łatwo przywrócić. Na przykład wyniki zapytań API są buforowane w Room lub DataStore w momencie otrzymania, a nie w onPause. Zapisuj tylko to, co użytkownik wprowadził ręcznie i czego nie można automatycznie przywrócić — tekst w polach, wybrane elementy, pozycję scrolla.
super.onPause() powinien być wywołany, ale w przeciwieństwie do onCreate, jego brak nie powoduje natychmiastowego crasha. System „wybacza” pominięcie super w onPause, ale wewnętrzny automat stanów przechodzi w nieprawidłowy stan. Następne wywołanie onResume może nie przywrócić fokusu wprowadzania, a Activity pozostanie „zamrożone”. Zawsze wywołuj super.onPause() jak najwcześniej.
Często zadawane pytania
Wywołanie finish() w onPause zakończy Activity natychmiast po powrocie z metody. Jest to poprawny scenariusz, jeśli przy utracie fokusu trzeba zamknąć ekran (na przykład ekran autoryzacji przy minimalizacji aplikacji). Jednak finish() uruchamia pełny cykl zakończenia: onStop → onDestroy, co dodaje opóźnienie do przejścia. Używaj finish() w onPause tylko gdy jest to naprawdę konieczne.
onPause — do zapisywania danych, które muszą przetrwać zakończenie procesu (szkice w SharedPreferences/Room). onSaveInstanceState — do zapisywania tymczasowego stanu UI, który jest potrzebny tylko do następnego onCreate (pozycja scrolla, wybrana zakładka). Bundle onSaveInstanceState nie jest zachowywany przy całkowitym zakończeniu aplikacji — istnieje tylko w pamięci. Dane onPause są zapisywane na dysku i przetrwają restart.
Nie jest zalecane. Otwarcie dialogu lub wyskakującego okna w onPause prowadzi do WindowLeakException, jeśli Activity jest już zakończone. Jeśli potrzebujesz pokazać powiadomienie przy utracie fokusu, użyj NotificationManager (powiadomienia systemowe) — jest to bezpieczne i oczekiwane przez użytkownika. Do odroczonych działań używaj AlarmManager lub WorkManager.
onPause jest gwarantowanie wywoływane przed tym, jak Activity przestanie być aktywne. onStop może nie zostać wywołane, jeśli system zabija proces w celu zwolnienia pamięci — w tym przypadku onDestroy również nie jest wywoływane. onPause to jedyna metoda po onResume, która jest wywoływana zawsze, niezależnie od przyczyny utraty fokusu. Dlatego wszystkie krytyczne dane zapisuje się właśnie w onPause.
Do testowania onPause używa się Robolectric lub FragmentScenario z AndroidX Test. FragmentScenario.create() → moveToState(State.STARTED) → moveToState(State.RESUMED) → moveToState(State.STARTED) sekwencyjnie wywołuje onPause. Następnie sprawdza się, czy dane zostały zapisane w SharedPreferences lub czy kamera została zwolniona przez mock-obiekt. Robolectric 4.12+ obsługuje emulację onPause/onResume bez fizycznego urządzenia.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również