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.
Google Android Performance Guide (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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође