onPause: ce este, salvarea stării Activity în Android

Autor: IT Sectr Publicat: 2026-03-04 Timp de citire: 10 min

onPause — este metoda ciclului de viață Android care este apelată atunci când Activity pierde focalizarea de intrare, dar rămâne parțial vizibilă pe ecran. Sistemul apelează onPause înainte ca o nouă Activity să iasă în prim-plan, la deschiderea unei ferestre de dialog, la apăsarea butonului „Aplicații recente” sau la un apel primit. Această metodă — ultimul punct garantat pentru salvarea datelor utilizatorului, deoarece după onStop și onDestroy sistemul poate termina procesul fără apeluri suplimentare. În onPause dezvoltatorul salvează ciorne, suspendă animațiile, eliberează camera și scrie starea curentă a UI în SharedPreferences. Mai multe despre ciclul de viață complet al Activity citiți în articolul Activity Lifecycle.

Principalele

  • onPause — Activity pierde focalizarea, dar rămâne vizibilă; ultimul punct garantat de salvare a datelor
  • Salvarea stării — în onPause se salvează datele critice ale utilizatorului: ciorne, text în formulare, progres
  • Eliberarea resurselor — camera, microfonul, playerul video se eliberează în onPause pentru transmiterea la o altă aplicație
  • Limita de timp — onPause trebuie să se finalizeze în 100 ms; depășirea cauzează ANR și întârzie tranziția
  • SharedPreferences.apply() — scriere asincronă în onPause; commit() blochează firul și poate cauza ANR
  • onPause vs onStop — onPause la vizibilitate parțială (dialog), onStop la ascunderea completă (altă Activity)
  • onSaveInstanceState — se apelează după onPause pentru salvarea stării temporare în Bundle

Ce este onPause în Android

onPause — a patra metodă a ciclului de viață Activity, care este apelată când ecranul pierde focalizarea de intrare, dar rămâne parțial vizibil pentru utilizator. Aceasta este o stare „de tranziție” între munca activă a aplicației și ascunderea ei. Sistemul apelează onPause în următoarele scenarii: deschiderea unei alte Activity (un ecran nou acoperă cel curent), apariția unei ferestre de dialog (Dialog, PopupWindow, Snackbar nu apelează onPause, dar DialogFragment — da), apăsarea butonului „Aplicații recente”, apel primit, apăsarea butonului „Alimentare” pentru blocarea ecranului.

Sarcina principală a onPause — pregătirea aplicației pentru posibila ascundere sau distrugere. Acesta este ultimul punct în ciclul de viață unde dezvoltatorul poate fi sigur că codul său se va executa înainte ca sistemul să continue tranziția către o altă componentă. După onPause sistemul apelează onStop (dacă Activity se ascunde complet), după care distrugerea procesului poate avea loc în orice moment fără notificări suplimentare.

Conform documentației Android Developers (2025), onPause trebuie să fie cât mai ușor și rapid. Până când onPause nu returnează controlul, sistemul nu poate porni următoarea Activity — aceasta înseamnă că utilizatorul vede o întârziere a tranziției între ecrane. Google recomandă finalizarea onPause în mai puțin de 100 de milisecunde, iar toate operațiile lungi (salvarea în baza de date, scrierea pe disc) să fie executate asincron prin corutine sau apply().

onPause în Activity

În Activity, metoda onPause este apelată de fiecare dată când ecranul încetează să fie activ, dar poate continua să fie afișat parțial. Exemplu tipic: utilizatorul deschide aplicația „Hărți”, apasă „Partajează locația”, și deasupra Hărților se deschide un dialog de sistem pentru selectarea aplicației. Activity Hărților primește onPause, dar rămâne vizibilă sub dialog. Când dialogul se închide, Hărțile primesc onResume fără apelarea onStart (ecranul nu a fost ascuns complet).

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

        // Salvăm ciorna notiței — asincron
        prefs.edit()
            .putString("draft_title", binding?.titleInput?.text.toString())
            .putString("draft_body", binding?.bodyInput?.text.toString())
            .putLong("draft_timestamp", System.currentTimeMillis())
            .apply()

        // Suspendăm videoclipul
        binding?.videoPlayer?.pause()

        // Eliberăm resursele exclusive
        releaseCamera()
        releaseAudioFocus()
    }

    override fun onResume() {
        super.onResume()
        // Restaurăm ciorna
        binding?.titleInput?.setText(prefs.getString("draft_title", ""))
        binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
        acquireCamera()
        acquireAudioFocus()
    }
}

Exemplul NoteEditorActivity demonstrează lucrul corect cu onPause: salvarea ciornei în SharedPreferences prin apply(), suspendarea fișierului video, eliberarea camerei și a focalizării audio. Fiecare apel — ușor și rapid, fără a bloca firul UI suficient pentru ANR. Atenție la ordine: super.onPause() este apelat în prima linie — aceasta garantează că logica de sistem se va executa chiar și la o excepție în codul utilizatorului.

Salvarea stării în onPause

onPause — ultimul punct unde dezvoltatorul poate salva garantat datele utilizatorului înainte ca aplicația să fie ascunsă sau ucisă de sistem. După onStop sistemul poate distruge procesul la lipsa de memorie fără a apela onDestroy. Metoda onSaveInstanceState() este apelată după onPause, dar Bundle-ul său nu este destinat stocării pe termen lung — trăiește doar până la următorul onCreate.

SharedPreferences cu apply()

SharedPreferences cu apply() asincron — modalitatea optimă de salvare a volumelor mici de date în onPause. Spre deosebire de commit(), care scrie sincron datele pe disc și returnează boolean, apply() salvează imediat datele în memorie și planifică o scriere asincronă pe disc. Aceasta durează mai puțin de 1 milisecundă în firul UI față de 10–100 milisecunde pentru commit().

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

    // ❌ Rău: scrierea sincronă blochează firul
    // prefs.edit().putInt("score", score).commit()

    // ✅ Bine: scrierea asincronă
    prefs.edit().putInt("score", score).apply()

    // Pentru obiecte complexe — stocare în cache în ViewModel
    viewModel.saveState()
}

Room și corutine

Pentru date structurate (SQLite prin Room) în onPause se folosesc corutine cu lifecycleScope. ViewModelScope anulează automat corutina la distrugerea ViewModel, prevenind scrierea într-o bază de date închisă. Scrierea prin Room cu corutine durează 5–15 milisecunde și nu blochează firul UI.

kotlin
// În ViewModel:
fun saveDraft(title: String, body: String) {
    viewModelScope.launch(Dispatchers.IO) {
        noteDao.insert(NoteDraft(title = title, body = body))
    }
}

// În Activity.onPause:
viewModel.saveDraft(
    binding?.titleInput?.text.toString(),
    binding?.bodyInput?.text.toString()
)

onPause în Fragment

onPause în Fragment este apelat când Fragmentul încetează să fie activ, dar poate rămâne vizibil. Aceasta se întâmplă când: Fragmentul este înlocuit cu un alt Fragment prin FragmentTransaction; Fragmentul încetează să fie pagina curentă în ViewPager; Activity care conține Fragmentul primește onPause. Interacțiunea între onPause al Activity și onPause al Fragmentului este strict ierarhică: mai întâi onPause primește Activity, apoi toate Fragmentele sale.

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

Specificul lucrului cu hărțile în onPause: Google Maps și Yandex Maps consumă resurse GPU semnificative în modul de urmărire activ (follow mode). La pierderea focalizării, are sens să dezactivați animația hărții și să reduceți frecvența de actualizare a marcajelor, iar la revenirea focalizării — să restabiliți funcționalitatea completă. Aceasta îmbunătățește performanța și reduce consumul de energie la comutarea între ecrane.

onPause vs onStop: diferența și scenariile

Una dintre cele mai frecvente confuzii la dezvoltatorii Android începători — neînțelegerea diferenței dintre onPause și onStop. Să examinăm fiecare scenariu și să determinăm metoda corectă.

ScenariuonPauseonStop
Deschiderea ferestrei de dialogApelatNeapelat
Deschiderea unei noi Activity (netransparente)ApelatApelat
Apăsarea butonului „Acasă”ApelatApelat
Blocarea ecranuluiApelatApelat
Apel primitApelatApelat
Activity transparentă peste cea curentăApelatNeapelat
Split Screen (jumătate de ecran)ApelatNeapelat
PiP (Picture-in-Picture)ApelatNeapelat

Regula principală: onPause este apelat la orice pierdere a focalizării, onStop — doar la pierderea completă a vizibilității. Dacă Activity rămâne vizibilă (chiar și parțial), onStop nu este apelat. Acest lucru este critic pentru modurile Split Screen, PiP și Activity transparente — aici onPause/onResume funcționează, iar onStart/onStop — nu.

Timpul și performanța onPause

onPause — cea mai critică metodă din punct de vedere al timpului a ciclului de viață, deoarece blochează randarea următoarei Activity. Sistemul așteaptă finalizarea onPause a Activity curente înainte de a arăta una nouă. Dacă onPause se execută mai mult de 100 de milisecunde, utilizatorul observă o întârziere a tranziției; dacă mai mult de 5 secunde — sistemul arată ANR.

Recomandări de performanță

Ghidul de performanță Android Google (2025) oferă următoarele recomandări pentru onPause: nu executați cereri de rețea — acestea trebuie anulate sau mutate în WorkManager; nu scrieți fișiere mari pe disc — utilizați BufferedWriter într-un fir de fundal; nu executați interogări SQL complexe — operațiile Room trebuie să fie asincrone prin corutine; evitați crearea de obiecte noi — garbage collection în onPause agravează întârzierea; utilizați apply() în loc de commit() pentru SharedPreferences.

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

    // ❌ Rău: cererea HTTP blochează UI
    // val response = api.syncSave(data).execute()

    // ❌ Rău: scrierea sincronă în fișier
    // FileOutputStream(file).write(data)

    // ✅ Bine: salvarea asincronă
    lifecycleScope.launch {
        withContext(Dispatchers.IO) {
            api.saveData(data)
            fileDao.write(data)
        }
    }

    // ✅ Bine: scrierea ușoară în SharedPreferences
    prefs.edit().putString("key", value).apply()
}

Profilarea onPause prin Android Studio Profiler (graficul CPU) arată timpul exact de execuție. Dacă onPause durează mai mult de 100 ms, Profiler evidențiază metoda cu galben, iar mai mult de 500 ms — cu roșu. În proiectele comerciale IT Sectr folosim teste Macrobenchmark care verifică automat timpul de tranziție între Activity și semnalează regresia de performanță în pipeline-ul CI.

Greșeli frecvente în onPause

Chiar și dezvoltatorii experimentați fac greșeli în onPause. Să examinăm cinci probleme tipice și soluțiile lor.

Scrierea sincronă în baza de date

Apelarea Room DAO cu o interogare sincronă (.executeAsObservable() fără corutine) în onPause blochează firul UI timp de 10–50 ms. Dacă în acest moment are loc GC sau concurență pentru scrierea în baza de date, întârzierea poate ajunge la 200–500 ms. Soluție: utilizați corutine cu Dispatchers.IO sau apply() pentru SharedPreferences.

Înregistrarea de noi ascultători

onPause nu este locul pentru înregistrarea ascultătorilor. Dacă înregistrați un BroadcastReceiver în onPause, acesta va rămâne activ când Activity nu mai este vizibilă. Înregistrarea trebuie să fie doar în onStart/onResume, iar în onPause/onStop — doar dezabonare. Excepție — API-uri Intent-driven care necesită înregistrare înainte de apelare.

Ignorarea excepțiilor

Dacă în onPause apare o excepție negestionată, sistemul nu apelează onStop și onDestroy. Activity rămâne blocată într-o stare nedeterminată, iar onResume la revenire poate să nu restaureze corect resursele eliberate. Soluție: înfășurați operațiile critice în try/catch cu logare prin Log.e().

Salvarea datelor redundante

Nu este necesar să salvați în onPause date care pot fi ușor restaurate. De exemplu, rezultatele cererilor API sunt stocate în cache în Room sau DataStore în momentul primirii, nu în onPause. Salvați doar ceea ce utilizatorul a introdus manual și nu poate restaura automat — text în câmpuri, elemente selectate, poziția scroll-ului.

Uitat super.onPause()

super.onPause() trebuie apelat, dar spre deosebire de onCreate, lipsa lui nu cauzează un crash imediat. Sistemul „iertă” omiterea super în onPause, dar mașina de stări internă trece într-o stare incorectă. Următorul apel onResume poate să nu restabilească focalizarea de intrare, iar Activity rămâne „înghețată”. Apelați întotdeauna super.onPause() cât mai devreme posibil.

Întrebări frecvente

Ce se întâmplă dacă apelezi finish() în onPause?

Apelarea finish() în onPause va încheia Activity imediat după revenirea din metodă. Acesta este un scenariu corect dacă la pierderea focalizării trebuie închis ecranul (de exemplu, ecranul de autentificare la minimizarea aplicației). Cu toate acestea, finish() pornește ciclul complet de încheiere: onStop → onDestroy, ceea ce adaugă o întârziere la tranziție. Utilizați finish() în onPause doar când este cu adevărat necesar.

Cu ce se deosebește onPause de onSaveInstanceState?

onPause — pentru salvarea datelor care trebuie să supraviețuiască încheierii procesului (ciorne în SharedPreferences/Room). onSaveInstanceState — pentru salvarea stării temporare a UI care este necesară doar până la următorul onCreate (poziția scroll-ului, fila selectată). Bundle-ul onSaveInstanceState nu se salvează la încheierea completă a aplicației — există doar în memorie. Datele onPause sunt salvate pe disc și supraviețuiesc repornirii.

Se poate deschide o fereastră de dialog în onPause?

Nu este recomandat. Deschiderea unui dialog sau a unei ferestre pop-up în onPause duce la WindowLeakException dacă Activity este deja încheiată. Dacă trebuie să afișați o notificare la pierderea focalizării, utilizați NotificationManager (notificări de sistem) — este sigur și așteptat de utilizator. Pentru acțiuni amânate, utilizați AlarmManager sau WorkManager.

De ce onPause este un loc garantat de salvare, iar onStop — nu?

onPause este garantat apelat înainte ca Activity să înceteze să fie activă. onStop poate să nu fie apelat dacă sistemul ucide procesul pentru a elibera memoria — în acest caz onDestroy nu este apelat nici el. onPause — singura metodă după onResume care este apelată întotdeauna, indiferent de cauza pierderii focalizării. De aceea toate datele critice se salvează exact în onPause.

Cum să testăm onPause în teste unitare?

Pentru testarea onPause se folosește Robolectric sau FragmentScenario din AndroidX Test. FragmentScenario.create() → moveToState(State.STARTED) → moveToState(State.RESUMED) → moveToState(State.STARTED) apelează secvențial onPause. Apoi se verifică dacă datele au fost salvate în SharedPreferences sau dacă camera a fost eliberată printr-un obiect mock. Robolectric 4.12+ suportă emularea onPause/onResume fără dispozitiv fizic.

Rezumat

  • onPause — Activity pierde focalizarea de intrare, dar rămâne parțial vizibilă; ultimul punct garantat de salvare a datelor
  • Salvarea — SharedPreferences.apply() sau Room prin corutine; commit() și operațiile sincrone sunt interzise
  • Eliberarea resurselor — camera, focalizarea audio, playerul video se eliberează în onPause pentru o altă aplicație
  • Limita de 100 ms — onPause blochează randarea următoarei Activity; depășirea limitei cauzează ANR
  • onPause vs onStop — onPause la pierderea focalizării (vizibilitatea păstrată), onStop la ascunderea completă
  • Fragment.onPause — apel ierarhic după Activity.onPause; specific pentru hărți și ViewPager
  • Greșeli tipice — scriere sincronă, înregistrarea ascultătorilor, ignorarea try/catch, salvarea redundantă
  • super.onPause() — apelați cât mai devreme; omiterea nu cauzează crash, dar strică mașina de stări

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și