Scurgere de memorie Android
Cum o gestionare corectă a ciclului de viață poate preveni scurgerile de memorie Android
OutOfMemoryException este un bug comun și frustrant, și una dintre cauzele principale ale închiderii neașteptate a aplicației.
„De ce se întâmplă acum, dacă aplicația funcționa perfect ieri?” Este o întrebare care îi lasă perplecși atât pe dezvoltatorii Android începători, cât și pe cei avansați din întreaga lume.
Există o varietate de cauze potențiale ale excepțiilor OutOfMemory Exceptions, dar una dintre cele mai frecvente este scurgerea de memorie – alocarea de memorie în aplicație care nu este niciodată eliberată. Acest articol va explica cum se poate minimiza acest risc prin gestionarea eficientă a ciclului de viață – o parte crucială, dar adesea neglijată, a procesului de dezvoltare.
TL;DR
În acest articol vom discuta despre:
- Motive pentru scurgerile de memorie Android
- Măsuri de bază pentru a le preveni
- Forme mai avansate de gestionare a ciclului de viață
- Instrumente care pot gestiona ciclul de viață al produsului dumneavoastră
De ce se întâmplă scurgerile de memorie RAM pe Android?
Problema este simplă. Anumite obiecte ar trebui să aibă doar o durată de viață fixă, iar când viața lor utilă se termină, trebuie să fie eliminate.
În teorie, această memorie ar trebui eliminată atunci când un proces este ucis folosind onStop sau onDestroy. Cu toate acestea, utilizarea greșită a referințelor la obiecte poate împiedica garbage collector-ul să elimine obiectele nefolosite. De exemplu: dacă obiectul nefolosit A face referire la obiectul nefolosit B, vă veți trezi cu două obiecte inutile pe care colectorul de gunoi nu le va dezaloca niciodată, deoarece se referă unul la celălalt.
Trucuri comune pentru a împiedica acest lucru să se întâmple
Dezvoltatorii pot lua o serie de măsuri pentru a împiedica activitățile moarte să fie blocate în memorie.
- Înregistrează/dezînregistrează BroadcastReceivers în onResume()/onPause() sau onStart()/onStop()
- NU folosiți NICIODATĂ variabile statice pentru Vizualizări/Activități/Contexte
- Singletons care au nevoie să dețină referințe la Context ar trebui să folosească applicationContext() sau să o înfășoare în WeakReference
- Aveți grijă cu activitățile anonime și non-anonime.statice interne, deoarece acestea dețin o referință implicită la clasa care le înconjoară.
- Utilizați clase interne statice în loc de clase anonime dacă vor supraviețui clasei părinte (cum ar fi Handlers).
- Dacă clasa internă sau anonimă este anulabilă (cum ar fi AsyncTask, Thread, RxSubscriptions), anulați-o atunci când activitatea este distrusă.
Componentele Android care iau în considerare ciclul de viață
După ce ați finalizat pașii de bază de mai sus, este timpul pentru ceva mult mai important: Ciclul de viață al activităților aplicației. Dacă nu gestionăm corect ciclul de viață, vom sfârși prin a ne agăța de memorie atunci când nu mai este nevoie de ea.
Există multe sarcini diferite implicate în acest sens. Pentru fiecare activitate, trebuie să întrerupem firele de execuție, să scăpăm de abonamente în RxJava, să anulăm referințele AsyncTask și să ne asigurăm că referința acestei activități (și a oricărei alte activități conectate la ea) este corect eliminată. Toate aceste sarcini pot absorbi o cantitate uriașă din timpul dezvoltatorului.
Lucrurile sunt și mai complicate de Model View Presenter (MVP), modelul arhitectural folosit adesea pentru a construi interfața cu utilizatorul în Android. Cu toate acestea, MVP este util pentru a decupla logica de afaceri de View.
În modelul MVP, atât View cât și Presenter sunt implementări abstracte ale unui contract pentru un comportament între ele. Cel mai comun mod de a implementa MVP este utilizarea unei Activități/Fragment ca o implementare pentru vizualizare și o implementare simplă pentru prezentator, care este obișnuit să aibă o referință a vizualizării.
Acum ajungem să avem o vizualizare cu o referință a prezentatorului și un prezentator cu o referință a vizualizării (Indicație: Avem o potențială scurgere aici).
Datorită acestor potențiale dificultăți, este esențial să punem în aplicare o structură de gestionare adecvată pentru a elimina excesul de memorie creat în timpul ciclului de viață. Există mai multe modalități dovedite de a face acest lucru:
Crearea de componente conștiente de ciclul de viață folosind Android Arch Lifecycle în Android Studio
Componentele conștiente de ciclul de viață sunt inteligente. Ele pot reacționa la o schimbare a stării ciclului de viață a unei alte componente, cum ar fi activitățile sau fragmentele, eliminând memoria, de exemplu. Acest lucru înseamnă că codul este mai ușor și mult mai eficient din punct de vedere al memoriei.
Arch Lifecycle este o nouă bibliotecă de la Android care oferă un set de instrumente pentru a construi componente conștiente de ciclul de viață. Biblioteca funcționează într-un mod abstract, ceea ce înseamnă că proprietarii de cicluri de viață nu mai trebuie să se preocupe de gestionarea ciclului de viață al unor sarcini și activități specifice.
Instrumentele și definițiile cheie ale Arch Lifecycle sunt următoarele:
- LifeCycle: Un sistem de sortare care definește ce obiecte au un ciclu de viață Android și permite ca acestea să fie apoi monitorizate.
- LifecycleObserver: O interfață obișnuită care monitorizează fiecare obiect identificat ca având un ciclu de viață Android, utilizând o formulă simplă pentru a gestiona fiecare eveniment cheie al ciclului de viață.
- @OnLifecycleEvent: O adnotare care poate fi utilizată în clasele care implementează interfața LifecycleObserver. Ea ne permite să stabilim evenimente cheie ale ciclului de viață care vor declanșa metoda adnotată ori de câte ori este lansată. Iată o listă a tuturor evenimentelor care pot fi setate:
- ON_ANY
- ON_CREATE
- ON_DESTROY
- ON_PAUSE
- ON_RESUME
- ON_START
- ON_RESUME
- ON_START
- ON_STOP
.
- LifecycleOwner este implementat în mod implicit pentru fiecare componentă Android al cărei ciclu de viață poate fi gestionat, și oferă dezvoltatorului controlul asupra fiecărui eveniment.
Utilizând aceste instrumente, suntem capabili să trimitem toate sarcinile curate către proprietarii lor (prezentatorii în cazul nostru), astfel încât avem un cod curat, decuplabil și lipsit de scurgeri (cel puțin în stratul prezentatorului).
Iată o implementare super-basică pentru a vă arăta despre ce vorbim:
interface View: MVPView, LifecycleOwnerclass RandomPresenter : Presenter<View>, LifecycleObserver { private lateinit var view: View override fun attachView(view: View) { this.view = view view.lifecycle.addObserver(this) } @OnLifecycleEvent(Lifecycle.Event.On_DESTROY) fun onClear() {//TODO: clean }
Utilizarea modelelor de vizualizare Android Arch ca prezentatori și LiveData
O altă modalitate de a evita scurgerile de memorie prin gestionarea defectuoasă a ciclului de viață este utilizarea noilor ViewModels din biblioteca Arch Components.
Un ViewModel este o clasă abstractă care implementează o singură funcție cunoscută sub numele de onClear, care este apelată automat atunci când un anumit obiect trebuie să fie eliminat. ViewModel este generat de framework și este atașat la ciclul de viață al creatorului (ca un bonus suplimentar, este foarte ușor de injectat cu Dagger.)
Pe lângă utilizarea ViewModel, LiveData oferă, de asemenea, un canal vital de comunicare. Produsul urmează un model reactiv, ușor de observat, ceea ce înseamnă că obiectele individuale îl pot observa (creând un cod mai puțin cuplat).
Un punct important aici este că LiveData poate fi observat de un proprietar al ciclului de viață, astfel încât transferul de date este întotdeauna gestionat de ciclul de viață -și ne putem asigura că orice referință este păstrată în timp ce le folosim.
Utilizarea LeakCanary și Bugfender
În plus față de pașii menționați mai sus, am vrut să recomandăm două piese importante de echipament: LeakCanary, o unealtă populară pentru monitorizarea scurgerilor de informații, și propriul nostru Bugfender.
LeakCanary este o bibliotecă de detectare a memoriei pentru Android și Java. Este open-source, deci există o comunitate uriașă în spatele ei, și nu vă spune doar despre o scurgere – vă informează despre cauza probabilă.
Bugfender, instrumentul nostru de logare de la distanță, vă permite să depanați LeakTraces individuale și să extindeți o clasă numită DisplayLeakService, care ne permite să știm când este ridicată o scurgere. Apoi, o putem înregistra cu ușurință cu Bugfender.
public class LeakUploadService extends DisplayLeakService { override fun afterDefaultHandling(heapDump: HeapDump, result: AnalysisResult, leakInfo: String) { if (result.leakFound) { Bugfender.d("LeakCanary", result.toString()) } }}
În plus, utilizatorii primesc toate celelalte beneficii ale Bugfender, inclusiv înregistrarea jurnalelor 24/7 (chiar și atunci când dispozitivul este offline), raportarea integrată a accidentelor și o consolă web ușor de utilizat, care permite detalierea dispozitivelor individuale pentru o mai bună îngrijire a clienților.
Pentru mai multe informații despre Bugfender, vă rugăm să faceți clic aici.
Leave a Reply