onPause: шта је то, чување стања Activity-ја у Android-у

Аутор: IT Sectr Објављено: 2026-03-04 Време читања: 10 мин

onPause — метод животног циклуса Android-а који се позива када Activity изгуби фокус уноса, али остаје дјелимично видљив на екрану. Систем позива onPause прије него што нови Activity изађе у први план, при отварању дијалошког окна, при притиску на дугме „Недавне апликације” или при долазном позиву. Овај метод — посљедња гарантована тачка за чување података корисника, јер након onStop и onDestroy систем може завршити процес без додатних позива. Унутар onPause програмер чува нацрте, зауставља анимације, ослобађа камеру и уписује тренутно стање UI-ја у SharedPreferences. Више о комплетном животном циклусу Activity-ја прочитајте у чланку Activity Lifecycle.

Главно

  • onPause — Activity губи фокус, али остаје видљив; посљедња гарантована тачка чувања података
  • Чување стања — у onPause чувају се критични подаци корисника: нацрти, текст у формама, напредак
  • Ослобађање ресурса — камера, микрофон, видео плејер ослобађају се у onPause за предају другој апликацији
  • Временско ограничење — onPause мора да се заврши за 100 ms; прекорачење изазива ANR и задржава прелаз
  • SharedPreferences.apply() — асинхрони упис у onPause; commit() блокира нит и може изазвати ANR
  • onPause vs onStop — onPause при дјелимичној видљивости (дијалог), onStop при потпуном скривању (други Activity)
  • onSaveInstanceState — позива се након onPause за чување привременог стања у Bundle

Шта је onPause у Android-у

onPause — четврти метод животног циклуса Activity-ја који се позива када екран изгуби фокус уноса, али остаје дјелимично видљив за корисника. Ово је „прелазно” стање између активног рада апликације и њеног скривања. Систем позива onPause у сљедећим сценаријима: отварање другог Activity-ја (нови екран прекрива тренутни), појављивање дијалошког окна (Dialog, PopupWindow, Snackbar не позивају onPause, а DialogFragment — позива), притисак на дугме „Недавне апликације”, долазни позив, притисак на дугме „Напајање” за закључавање екрана.

Основни задатак onPause — припрема апликације за могућност да буде скривена или уништена. Ово је посљедња тачка у животном циклусу гдје програмер може бити сигуран да ће се његов код извршити прије него систем настави прелаз на другу компоненту. Након onPause систем позива onStop (ако се Activity потпуно скрива), након чега уништење процеса може наступити у било ком тренутку без додатних обавјештења.

Према документацији Android Developers (2025), onPause мора бити максимално лаган и брз. Док onPause не врати контролу, систем не може покренути сљедећи Activity — то значи да корисник види кашњење прелаза између екрана. Google препоручује да се onPause заврши за мање од 100 милисекунди, а све дуготрајне операције (чување у базу података, упис на диск) извршавају асинхроно преко корутина или apply().

onPause у Activity-ју

У Activity-ју метод onPause се позива сваки пут када екран престане да буде активан, али може наставити да се дјелимично приказује. Типичан примјер: корисник отвара апликацију „Карте”, притиска „Подијели локацију”, и изнад Карита се отвара системски дијалог избора апликације. Activity Карита добија onPause, али остаје видљив испод дијалога. Када се дијалог затвори, Карте добијају onResume без позива onStart (екран није био потпуно скривен).

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

        // Чувамо нацрт биљешке — асинхроно
        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

onPause — посљедња тачка гдје програмер може гарантовано сачувати податке корисника прије него апликација буде сакривена или убијена од стране система. Након onStop систем може уништити процес при недостатку меморије без позива onDestroy. Метод onSaveInstanceState() се позива након onPause, али његов Bundle није намијењен дугорочном складиштењу — живи само до сљедећег onCreate.

SharedPreferences са apply()

SharedPreferences са асинхроним apply() — оптималан начин чувања малих количина података у onPause. За разлику од commit(), који синхроно уписује податке на диск и враћа boolean, apply() одмах чува податке у меморији и планира асинхрони упис на диск. Ово траје мање од 1 милисекунде у UI нити наспрам 10–100 милисекунди код commit().

kotlin
override fun onPause() {
    super.onPause()

    // ❌ Лоше: синхрони упис блокира нит
    // prefs.edit().putInt("score", score).commit()

    // ✅ Добро: асинхрони упис
    prefs.edit().putInt("score", score).apply()

    // За сложене објекте — кеширање у ViewModel
    viewModel.saveState()
}

Room и корутине

За структуриране податке (SQLite преко Room-а) у onPause користе се корутине са lifecycleScope. ViewModelScope аутоматски отказује корутину при уништењу ViewModel-а, што спречава упис у затворену базу података. Упис преко Room-а са корутинама траје 5–15 милисекунди и не блокира UI нит.

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

onPause у Fragment-у се позива када Fragment престане да буде активан, али може остати видљив. Ово се дешава када: Fragment бива замијењен другим Fragment-ом преко FragmentTransaction; Fragment престаје да буде тренутна страница у ViewPager-у; Activity који садржи Fragment добија onPause. Интеракција између onPause Activity-ја и onPause Fragment-а је строго хијерархијска: прво onPause добија Activity, затим сви његови Fragmentи.

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

Специфика рада са мапама у onPause: Google Maps и Yandex Maps троше значајне GPU ресурсе у активном режиму праћења (follow mode). При губитку фокуса има смисла искључити анимацију мапе и смањити учесталост освјежавања маркера, а при повратку фокуса — вратити пуну функционалност. Ово побољшава перформансе и смањује потрошњу енергије при пребацивању између екрана.

onPause vs onStop: разлика и сценарији

Једна од најчешћих забуна код почетника Android програмера — неразумијевање разлике између onPause и onStop. Размотримо сваки сценариј и одредимо прави метод.

СценаријonPauseonStop
Отварање дијалошког окнаПозива сеНе позива се
Отварање новог Activity-ја (не прозирног)Позива сеПозива се
Притисак на дугме „Почетна”Позива сеПозива се
Закључавање екранаПозива сеПозива се
Долазни позивПозива сеПозива се
Прозирни Activity изнад тренутногПозива сеНе позива се
Split Screen (пола екрана)Позива сеНе позива се
PiP (Picture-in-Picture)Позива сеНе позива се

Главно правило: onPause се позива при сваком губитку фокуса, onStop — само при потпуном губитку видљивости. Ако Activity остане видљив (чак и дјелимично), onStop се не позива. Ово је критично за режиме Split Screen, PiP и прозирне Activity-је — овдје onPause/onResume раде, а onStart/onStop — не.

Тајминг и перформансе onPause

onPause — најкритичнији по времену метод животног циклуса, јер блокира приказивање сљедећег Activity-ја. Систем чека да се заврши onPause тренутног Activity-ја прије него што прикаже нови. Ако onPause траје дуже од 100 милисекунди, корисник примјећује кашњење прелаза; ако дуже од 5 секунди — систем приказује ANR.

Препоруке за перформансе

Google Android Performance Guide (2025) даје сљедеће препоруке за onPause: не извршавајте мрежне захтјеве — они треба да буду отказани или премјештени у WorkManager; не уписујте велике датотеке на диск — користите BufferedWriter у позадинској нити; не извршавајте сложене SQL упите — Room операције треба да буду асинхроне преко корутина; избјегавајте стварање нових објеката — сакупљање смећа у onPause погоршава кашњење; користите apply() умјесто commit() за SharedPreferences.

kotlin
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

Чак и искусни програмери праве грешке у 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()

super.onPause() треба да се позове, али за разлику од onCreate, његово одсуство не изазива тренутни краш. Систем „прашта” пропуст super у onPause, али унутрашња машина стања прелази у неисправно стање. Сљедећи позив onResume можда неће обновити фокус уноса, и Activity остаје „замрзнут”. Увијек позивајте super.onPause() што је прије могуће.

Често постављана питања

Шта ће се десити ако се позове finish() у onPause?

Позив finish() у onPause завршиће Activity одмах након повратка из метода. Ово је исправан сценариј ако при губитку фокуса треба затворити екран (на примјер, екран ауторизације при минимизацији апликације). Међутим, finish() покреће пуни циклус завршетка: onStop → onDestroy, што додаје кашњење прелазу. Користите finish() у onPause само када је то стварно неопходно.

По чему се onPause разликује од onSaveInstanceState?

onPause — за чување података који треба да преживе завршетак процеса (нацрти у SharedPreferences/Room). onSaveInstanceState — за чување привременог стања UI-ја које је потребно само до сљедећег onCreate (позиција скрола, изабрана картица). Bundle onSaveInstanceState се не чува при потпуном завршетку апликације — постоји само у меморији. onPause подаци се чувају на диску и преживљавају поновно покретање.

Може ли се отворити дијалошко окно у onPause?

Не препоручује се. Отварање дијалога или искачућег прозора у onPause доводи до WindowLeakException ако је Activity већ завршен. Ако треба приказати обавјештење при губитку фокуса, користите NotificationManager (системска обавјештења) — то је сигурно и очекивано за корисника. За одложене радње користите AlarmManager или WorkManager.

Зашто је onPause гарантовано мјесто чувања, а onStop — није?

onPause се гарантовано позива прије него Activity престане да буде активан. onStop можда неће бити позван ако систем убије процес ради ослобађања меморије — у том случају onDestroy такође није позван. onPause — једини метод након onResume који се позива увијек, без обзира на разлог губитка фокуса. Због тога се сви критични подаци чувају управо у onPause.

Како тестирати 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 без физичког уређаја.

Резиме

  • onPause — Activity губи фокус уноса, али остаје дјелимично видљив; посљедња гарантована тачка чувања података
  • Чување — SharedPreferences.apply() или Room преко корутина; commit() и синхроне операције су забрањене
  • Ослобађање ресурса — камера, аудио фокус, видео плејер ослобађају се у onPause за другу апликацију
  • Ограничење 100 ms — onPause блокира приказивање сљедећег Activity-ја; прекорачење границе изазива ANR
  • onPause vs onStop — onPause при губитку фокуса (видљивост сачувана), onStop при потпуном скривању
  • Fragment.onPause — хијерархијски позив након Activity.onPause; специфика за мапе и ViewPager
  • Типичне грешке — синхрони упис, регистрација слушалаца, игнорисање try/catch, сувишно чување
  • super.onPause() — позвати што је прије могуће; пропуст не изазива краш, али квари машину стања

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође