Când un fragment este înlocuit și pus în stiva din spate (sau îndepărtat), rămâne în memorie?

Este comportamentul similar cu modul în care funcționează Activitățile? De exemplu, cu Activities funcționează astfel:

Activitatea A pornește Activitatea B, în timp ce B este pe ecran, sistemul poate elimina A din memorie dacă este nevoie de sistem. După apăsarea ÎNAPOI, A va fi recreată în memorie ca și cum nu a părăsit niciodată.

Am căutat o explicație clară a ceea ce se întâmplă din punct de vedere al memoriei cu Fragments și nu am găsit nimic. Funcționează la fel? De exemplu:

Activitatea C are Fragmentul F în aspect. Apoi, la un moment dat, F este înlocuit cu Fragmentul G, dar F este păstrat în stiva din spate.

Va rămâne F în memorie până când C este ucis sau poate fi eliminat de sistem după cum este necesar?

Într-adevăr, ceea ce mă întreb este dacă risc sau nu să rămân fără memorie dacă am o stivă de fragmente complicate într-o singură activitate?


person cottonBallPaws    schedule 12.12.2011    source sursă
comment
Rețineți că prima dvs. presupunere este incorectă: Activitatea A nu va fi niciodată eliminată din memorie. Consultați commonsware.com/blog /2011/10/03/   -  person Thierry-Dimitri Roy    schedule 01.03.2012
comment
@Thierry-DimitriRoy, incredibil... Simt că am trăit o minciună... aproape că nu cred...   -  person cottonBallPaws    schedule 04.03.2012
comment
Sistemul nu distruge o singură activitate sau fragment. Poate ucide întregul proces pentru a recupera memoria doar atunci când se epuizează.   -  person stdout    schedule 02.04.2019


Răspunsuri (4)


Aruncă o privire la aceasta: BackStackRecord.Op.fragment

Așa sunt stocate fragmentele în stiva din spate. Rețineți referința live, nici WeakReference și nici SoftReference nu sunt folosite acolo.

Acum aceasta: FragmentManagerImpl.mBackStack

Acolo este locul în care managerul stochează stiva din spate. ArrayList simplu, de asemenea, fără WR sau SR.

Și, în sfârșit, aceasta: Activity.mFragments

Aceasta este referința la managerul de fragmente.

GC poate colecta numai obiecte care nu au referințe live (nu sunt accesibile din niciun fir). Aceasta înseamnă că până când Activitatea dvs. este distrusă (și astfel, referința FragmentManager dispare), GC nu va putea colecta niciunul dintre Fragmentele din stiva din spate.

Rețineți că atunci când Activitatea este distrusă și reține starea (ca atunci când treceți dispozitivul în modul peisaj), nu păstrează Fragment obiecte reale în stivă, numai stările lor - Obiectele Fragment.FragmentState, adică fragmentele reale din stiva din spate sunt recreate de fiecare dată când activitatea este recreată cu starea reținută.

Sper că acest lucru vă ajută.

PS Deci, pe scurt: Da, puteți rămâne fără memorie adăugând Fragments la stiva din spate, precum și adăugând prea multe vizualizări pentru a vizualiza ierarhia.

UPD Luând în considerare exemplul dvs., F va rămâne în memorie până când C este ucis. Dacă C este ucis și apoi reînviat cu o configurație diferită - F va fi distrus și reîncarnat și într-un obiect diferit. Deci, amprenta de memorie a F este în jur până când C își pierde starea sau stiva din spate este ștearsă.

person Ivan Bartsov    schedule 09.02.2012
comment
Rețineți totuși că atunci când un fragment este pus pe stiva din spate, este apelat onDestroyView() al acestuia. Dacă în acel moment curățați orice alocări majore deținute de fragment, nu ar trebui să aveți probleme de memorie. - person hackbod; 11.02.2012
comment
Da, punct solid. Obiectele fragmentate vor ocupa totuși o anumită cantitate de mem și, strict vorbind, încă se poate inunda memoria cu o cantitate imensă de fragmente, chiar dacă logica de curățare este în vigoare, dar cu siguranță aveți dreptate - dacă sunt curățate corect, în condiții reale ( adică atunci când cantitatea de fragmente este sensibilă) fragmentele din stiva din spate nu ar trebui să provoace o supraîncărcare considerabilă a memoriei. - person Ivan Bartsov; 12.02.2012
comment
@hackbod Ce vrei să spui prin major allocations? - person theblang; 08.05.2014
comment
@mattblang Dacă îmi permiteți, cel mai probabil Dianne a vrut să spună ceva cu memorie grea și care nu este gestionat de ierarhia de vizualizare a fragmentului, cel mai notabil exemplu fiind Bitmaps. Dacă fragmentul tău se menține pe câteva primitive sau Strings de dimensiune medie -- probabil că este OK. Dar eliminați orice, în mai multe cazuri, care v-ar duce peste limita de memorie de proces. - person Ivan Bartsov; 08.05.2014
comment
@hackbod Este, de asemenea, necesar să anulăm referințele View ale fragmentului? Adică, dacă fragmentul este distrus (se apelează onDestroyView()) și GC dorește să colecteze gunoi, vor fi colectate vizualizările vechiului fragment dacă referințele la vizualizări rămân în fragment (scăpare de memorie)? Sau nu contează pentru că dacă fragmentul este distrus, atunci toate obiectele holt cu fragment (numai cu fragment) pot fi colectate de GC? - person traninho; 05.11.2014
comment
@traninho Bună idee. Când este pusă în stiva, o instanță Fragment nu este distrusă, așa că onDestroyView() ar trebui să anuleze întotdeauna orice referință de vizualizare pe care o are. În caz contrar, va împiedica vizionările moarte să primească GC. De asemenea, fragmentele reținute non-backstack (vezi setRetainInstance(true)) nu sunt distruse nici măcar atunci când Activity este recreat, așa că păstrând referințele de vizualizare puteți scurge un întreg Activity. În mod normal, totuși, acest lucru nu se întâmplă, deoarece în 99% din cazuri referințele de vizualizare sunt actualizate în onCreateView(), care sunt apelate la recrearea Actvity, deci după aceea, refurile indică oricum noi vizualizări. - person Ivan Bartsov; 07.11.2014
comment
@IvanBartsov Vă mulțumim pentru răspuns. În prezent, mă confrunt cu OOM în aplicația mea. Pentru simplitate, am 1 activitate (A) și un fragment (F) care deține viewpager cu alte 2 fragmente (F1 și F2). Din F1, dacă utilizatorul face clic pe un buton, atunci o altă instanță de (F) este deschisă din A și adăugată la un backstack. Deci, dacă utilizatorul face acest lucru de mai multe ori, atunci OOM va apărea mai devreme sau mai târziu. Voi încerca să anulez fiecare referință de vizualizare în onDestroy(). - person traninho; 07.11.2014
comment
@traninho Ei bine, chestii de genul acesta nu pot fi ușor de diagnosticat fără cod. Ar trebui să ducem asta într-o cameră de chat dacă doriți mai mult ajutor. - person Ivan Bartsov; 15.12.2014
comment
@IvanBartsov Nu, nu este necesar, eram doar curios. Dar multumesc oricum! :) - person traninho; 05.01.2015
comment
Cel mai bun răspuns, contractz! - person Natan Lotério; 01.10.2015
comment
@IvanBartsov Cred că am ratat un punct aici. Obiectul FragmentState este cel care este reținut numai în timpul modificărilor de configurare, nu fragmentul real corespunzător. Dar obiectul FragmentState conține o referință (mInstance) la fragmentul său și este unul puternic. Deci, cum reușește GC să distrugă fragmentul fără a-i distruge obiectul de stare, care include o referință la fragmentul său. - person stdout; 26.05.2017

Îmi pare rău că nu am putut să vă ofer vreo sursă oficială de informații, dar și eu eram curios să văd ce se va întâmpla și am decis să o testez. Și conform testelor mele, da, riscați să rămâneți fără memorie.

A trebuit să adaug o cantitate incredibilă de fragmente (mai mult de o sută) într-o buclă for pentru ca OutOfMemoryError să se întâmple, dar s-a întâmplat. Și verificându-mi jurnalele, am putut vedea că metodele onCreate() și onCreateView() au fost apelate de multe ori, dar onSaveInstance(), onPause() și onDestroy nu au fost apelate deloc.

Pentru referință, așa am adăugat fragmentele la backstack:

getSupportFragmentManager().beginTransaction().add(R.id.scene_fragment_container, mSceneFragment).addToBackStack("FOOBAR").commit();

Iar Fragmentele pe care le-am adăugat au fost oarecum simple: un ImageView, un EditText, un cuplu TextViews, un SeekBar și un ListView.

Dar dacă nu dețineți o cantitate mare de date în memorie, nu ar trebui să fie o problemă.

Mai târziu am încercat să adaug doar 50 la backstack, omorând aplicația și repornind-o. Și așa cum am sperat/ghicit, toate fragmentele au fost restaurate (și metodele onSaveInstance() și onPause() numite), așa că implementarea ciclului meu de viață nu a fost problema care a cauzat declanșarea OutOfMemoryError.

person Beowulf Bjornson    schedule 07.02.2012

De la developer.android.com/guide/topics/fundamentals/fragments.html

Un fragment trebuie să fie întotdeauna încorporat într-o activitate, iar ciclul de viață al fragmentului este direct afectat de ciclul de viață al activității gazdă. De exemplu, atunci când activitatea este întreruptă, la fel sunt toate fragmentele din ea, iar când activitatea este distrusă, la fel sunt toate fragmentele. Cu toate acestea, în timp ce o activitate rulează (este în starea ciclului de viață reluat), puteți manipula fiecare fragment în mod independent, cum ar fi adăugarea sau eliminarea acestora. Când efectuați o astfel de tranzacție cu fragmente, o puteți adăuga și la o stivă din spate care este gestionată de activitate - fiecare intrare din stiva din spate din activitate este o înregistrare a tranzacției cu fragmente care a avut loc. Stack-ul din spate permite utilizatorului să inverseze o tranzacție de fragment (navigați înapoi), prin apăsarea butonului BACK.

person Bill Gary    schedule 13.12.2011
comment
Mulțumesc Bill. Totuși, asta încă nu spune într-un fel sau altul dacă Fragmentele din stiva din spatele activităților sunt păstrate în memorie sau eliberate după cum este necesar, și asta este ceea ce mă întreb... - person cottonBallPaws; 13.12.2011
comment
@littleFluffyKitty de ce nu răspunde asta la întrebarea ta? Evident că sunt în memorie, dar întrerupte la fel ca o activitate obișnuită, nu sunt curățate până când te întorci sau activitatea ta este distrusă. - person Warpzit; 08.02.2012

Da, puteți rămâne fără memorie creând prea multe fragmente într-o singură activitate. Fragmentele vor fi distruse numai atunci când activitatea care le conține este.

person Sid    schedule 10.02.2012