onPause — метод от жизнения цикъл на Android, който се извиква, когато Activity губи фокуса за въвеждане, но остава частично видимо на екрана. Системата извиква onPause преди ново Activity да излезе на преден план, при отваряне на диалогов прозорец, при натискане на бутона „Последни приложения” или при входящо повикване. Този метод — последната гарантирана точка за запазване на потребителски данни, тъй като след onStop и onDestroy системата може да прекрати процеса без допълнителни извиквания. Вътре в onPause разработчикът запазва чернови, спира анимации, освобождава камерата и записва текущото състояние на UI в SharedPreferences. Повече за пълния жизнен цикъл на Activity прочетете в статията Activity Lifecycle.
Основни точки
onPause — четвъртият метод от жизнения цикъл на Activity, който се извиква, когато екранът губи фокуса за въвеждане, но остава частично видим за потребителя. Това е „преходно” състояние между активната работа на приложението и скриването му. Системата извиква onPause в следните сценарии: отваряне на друго Activity (нов екран покрива текущия), появяване на диалогов прозорец (Dialog, PopupWindow, Snackbar не извикват onPause, но DialogFragment извиква), натискане на бутона „Последни приложения”, входящо повикване, натискане на бутона „Захранване” за заключване на екрана.
Основната задача на onPause — да подготви приложението за възможността да бъде скрито или унищожено. Това е последната точка в жизнения цикъл, където разработчикът може да бъде сигурен, че кодът му ще се изпълни, преди системата да продължи прехода към друг компонент. След onPause системата извиква onStop (ако Activity се скрива напълно), след което унищожаването на процеса може да настъпи по всяко време без допълнителни уведомления.
Според документацията на Android Developers (2025), onPause трябва да бъде максимално лек и бърз. Докато onPause не върне контрола, системата не може да стартира следващото Activity — това означава, че потребителят вижда забавяне на прехода между екраните. Google препоръчва onPause да завършва за по-малко от 100 милисекунди, а всички продължителни операции (запазване в база данни, запис на диск) да се изпълняват асинхронно чрез корутини или apply().
В Activity методът onPause се извиква всеки път, когато екранът престане да бъде активен, но може да продължи да се показва частично. Типичен пример: потребителят отваря приложението „Карти”, натиска „Споделяне на местоположение” и над Картите се отваря системен диалог за избор на приложение. Activity на Картите получава onPause, но остава видимо под диалога. Когато диалогът се затвори, Картите получават onResume без извикване на onStart (екранът не беше напълно скрит).
class NoteEditorActivity : AppCompatActivity() {
private var binding: ActivityNoteEditorBinding? = null
private val prefs by lazy {
getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
}
override fun onPause() {
super.onPause()
// Запазваме черновата на бележката — асинхронно
prefs.edit()
.putString("draft_title", binding?.titleInput?.text.toString())
.putString("draft_body", binding?.bodyInput?.text.toString())
.putLong("draft_timestamp", System.currentTimeMillis())
.apply()
// Спираме видеото
binding?.videoPlayer?.pause()
// Освобождаваме ексклузивни ресурси
releaseCamera()
releaseAudioFocus()
}
override fun onResume() {
super.onResume()
// Възстановяваме черновата
binding?.titleInput?.setText(prefs.getString("draft_title", ""))
binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
acquireCamera()
acquireAudioFocus()
}
}
Примерът NoteEditorActivity демонстрира правилна работа с onPause: запазване на черновата в SharedPreferences чрез apply(), спиране на видео файла, освобождаване на камерата и аудио фокуса. Всяко извикване — леко и бързо, без да блокира UI нишката достатъчно за ANR. Обърнете внимание на реда: super.onPause() се извиква на първия ред — това гарантира, че системната логика ще се изпълни дори при изключение в потребителския код.
onPause — последната точка, където разработчикът може гарантирано да запази потребителски данни, преди приложението да бъде скрито или убито от системата. След onStop системата може да унищожи процеса при недостиг на памет без да извика onDestroy. Методът onSaveInstanceState() се извиква след onPause, но неговият Bundle не е предназначен за дългосрочно съхранение — живее само до следващото onCreate.
SharedPreferences с асинхронен apply() — оптималният начин за запазване на малки обеми данни в onPause. За разлика от commit(), който синхронно записва данни на диск и връща boolean, apply() незабавно запазва данни в паметта и планира асинхронен запис на диск. Това отнема по-малко от 1 милисекунда в UI нишката срещу 10–100 милисекунди при commit().
override fun onPause() {
super.onPause()
// ❌ Лошо: синхронният запис блокира нишката
// prefs.edit().putInt("score", score).commit()
// ✅ Добре: асинхронен запис
prefs.edit().putInt("score", score).apply()
// За сложни обекти — кеширане в ViewModel
viewModel.saveState()
}
За структурирани данни (SQLite чрез Room) в onPause се използват корутини с lifecycleScope. ViewModelScope автоматично отменя корутината при унищожаване на ViewModel, което предотвратява запис в затворена база данни. Записът чрез Room с корутини отнема 5–15 милисекунди и не блокира UI нишката.
// Във ViewModel:
fun saveDraft(title: String, body: String) {
viewModelScope.launch(Dispatchers.IO) {
noteDao.insert(NoteDraft(title = title, body = body))
}
}
// В Activity.onPause:
viewModel.saveDraft(
binding?.titleInput?.text.toString(),
binding?.bodyInput?.text.toString()
)
onPause във Fragment се извиква, когато Fragment престане да бъде активен, но може да остане видим. Това се случва, когато: Fragment бъде заменен от друг Fragment чрез FragmentTransaction; Fragment престане да бъде текущата страница във ViewPager; Activity, съдържащо Fragment, получи onPause. Взаимодействието между onPause на Activity и onPause на Fragment е строго йерархично: първо onPause получава Activity, след това всички негови Fragmentи.
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()
}
}
}
Специфика на работа с карти в onPause: Google Maps и Yandex Maps консумират значителни GPU ресурси в активен режим на проследяване (follow mode). При загуба на фокус има смисъл да изключите анимацията на картата и да намалите честотата на обновяване на маркерите, а при връщане на фокуса — да възстановите пълната функционалност. Това подобрява производителността и намалява консумацията на енергия при превключване между екрани.
Едно от най-честите обърквания сред начинаещите Android разработчици — неразбирането на разликата между onPause и onStop. Нека разгледаме всеки сценарий и да определим правилния метод.
| Сценарий | onPause | onStop |
|---|---|---|
| Отваряне на диалогов прозорец | Извиква се | Не се извиква |
| Отваряне на ново Activity (не прозрачно) | Извиква се | Извиква се |
| Натискане на бутона „Начало” | Извиква се | Извиква се |
| Заключване на екрана | Извиква се | Извиква се |
| Входящо повикване | Извиква се | Извиква се |
| Прозрачно Activity над текущото | Извиква се | Не се извиква |
| Split Screen (половин екран) | Извиква се | Не се извиква |
| PiP (Picture-in-Picture) | Извиква се | Не се извиква |
Основно правило: onPause се извиква при всяка загуба на фокус, onStop — само при пълна загуба на видимост. Ако Activity остане видимо (дори частично), onStop не се извиква. Това е критично за режимите Split Screen, PiP и прозрачни Activity — тук onPause/onResume работят, а onStart/onStop — не.
onPause — най-критичният по време метод от жизнения цикъл, тъй като блокира изобразяването на следващото Activity. Системата изчаква завършването на onPause на текущото Activity, преди да покаже ново. Ако onPause отнеме повече от 100 милисекунди, потребителят забелязва забавяне на прехода; ако повече от 5 секунди — системата показва ANR.
Ръководството за производителност на Android на Google (2025) дава следните препоръки за onPause: не изпълнявайте мрежови заявки — те трябва да бъдат отменени или преместени в WorkManager; не записвайте големи файлове на диск — използвайте BufferedWriter във фонова нишка; не изпълнявайте сложни SQL заявки — Room операциите трябва да бъдат асинхронни чрез корутини; избягвайте създаването на нови обекти — събиране на отпадъци в onPause влошава забавянето; използвайте apply() вместо commit() за SharedPreferences.
override fun onPause() {
super.onPause()
// ❌ Лошо: HTTP заявката блокира UI
// val response = api.syncSave(data).execute()
// ❌ Лошо: синхронен запис във файл
// FileOutputStream(file).write(data)
// ✅ Добре: асинхронно запазване
lifecycleScope.launch {
withContext(Dispatchers.IO) {
api.saveData(data)
fileDao.write(data)
}
}
// ✅ Добре: лек запис в SharedPreferences
prefs.edit().putString("key", value).apply()
}
Профилирането на onPause чрез Android Studio Profiler (CPU графика) показва точното време на изпълнение. Ако onPause отнеме повече от 100 ms, Profiler отбелязва метода в жълто, а повече от 500 ms — в червено. В комерсиалните проекти на IT Sectr използваме Macrobenchmark тестове, които автоматично проверяват времето за преход между Activity и сигнализират за регресия на производителността в CI пайплайна.
Дори опитни разработчици правят грешки в onPause. Нека разгледаме пет типични проблема и техните решения.
Извикването на Room DAO със синхронна заявка (.executeAsObservable() без корутини) в onPause блокира UI нишката за 10–50 ms. Ако в този момент настъпи GC или конкуренция за запис в базата данни, забавянето може да достигне 200–500 ms. Решение: използвайте корутини с Dispatchers.IO или apply() за SharedPreferences.
onPause не е място за регистриране на слушатели. Ако регистрирате BroadcastReceiver в onPause, той ще остане активен, когато Activity вече не е видимо. Регистрирането трябва да бъде само в onStart/onResume, а в onPause/onStop — само отписване. Изключение — Intent-driven API, които изискват регистрация преди извикване.
Ако в onPause възникне необработено изключение, системата не извиква onStop и onDestroy. Activity замръзва в неопределено състояние и onResume при връщане може да не възстанови правилно освободените ресурси. Решение: обвийте критичните операции в try/catch с логване чрез Log.e().
Не е необходимо да запазвате в onPause данни, които лесно могат да бъдат възстановени. Например, резултатите от API заявки се кешират в Room или DataStore в момента на получаване, а не в onPause. Запазвайте само това, което потребителят е въвел ръчно и не може да възстанови автоматично — текст в полета, избрани елементи, позиция на скрол.
super.onPause() трябва да се извика, но за разлика от onCreate, липсата му не причинява незабавен срив. Системата „прощава” пропускането на super в onPause, но вътрешната машина на състоянията преминава в неправилно състояние. Следващото извикване на onResume може да не възстанови фокуса за въвеждане и Activity остава „замръзнало”. Винаги извиквайте super.onPause() възможно най-рано.
Често задавани въпроси
Извикването на finish() в onPause ще прекрати Activity незабавно след връщане от метода. Това е правилен сценарий, ако при загуба на фокус трябва да се затвори екранът (например екран за авторизация при минимизиране на приложението). Въпреки това, finish() стартира пълния цикъл на прекратяване: onStop → onDestroy, което добавя забавяне към прехода. Използвайте finish() в onPause само когато е наистина необходимо.
onPause — за запазване на данни, които трябва да преживеят прекратяването на процеса (чернови в SharedPreferences/Room). onSaveInstanceState — за запазване на временно състояние на UI, което е необходимо само до следващото onCreate (позиция на скрол, избран раздел). Bundle на onSaveInstanceState не се запазва при пълно прекратяване на приложението — съществува само в паметта. Данните на onPause се запазват на диск и преживяват рестартиране.
Не се препоръчва. Отварянето на диалог или изскачащ прозорец в onPause води до WindowLeakException, ако Activity вече е прекратено. Ако трябва да покажете известие при загуба на фокус, използвайте NotificationManager (системни известия) — това е безопасно и очаквано от потребителя. За отложени действия използвайте AlarmManager или WorkManager.
onPause се извиква гарантирано, преди Activity да престане да бъде активно. onStop може да не бъде извикан, ако системата убие процеса за освобождаване на памет — в този случай onDestroy също не се извиква. onPause е единственият метод след onResume, който се извиква винаги, независимо от причината за загуба на фокус. Ето защо всички критични данни се запазват точно в onPause.
За тестване на onPause се използва Robolectric или FragmentScenario от AndroidX Test. FragmentScenario.create() → moveToState(State.STARTED) → moveToState(State.RESUMED) → moveToState(State.STARTED) последователно извиква onPause. След това се проверява дали данните са запазени в SharedPreferences или дали камерата е освободена чрез mock обект. Robolectric 4.12+ поддържа емулация на onPause/onResume без физическо устройство.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също