Android hukommelseslækage
Hvordan korrekt livscyklusstyring kan forhindre Android-hukommelseslækager
OutOfMemoryException er en almindelig og frustrerende fejl og en af de vigtigste årsager til uventet lukning af apps.
“Hvorfor sker det nu, hvis appen fungerede perfekt i går?” Det er et spørgsmål, der forvirrer både nybegyndere og avancerede Android-udviklere verden over.
Der er en række potentielle årsager til OutOfMemory Exceptions, men en af de mest almindelige er hukommelseslækage – allokering af hukommelse i appen, som aldrig frigives. I denne artikel forklares det, hvordan denne risiko kan minimeres gennem effektiv livscyklusstyring – en afgørende, men ofte overset del af udviklingsprocessen.
TL;DR
I denne artikel vil vi diskutere:
- Grundene til Android-hukommelseslækage
- Grundlæggende trin til at forhindre det
- Mere avancerede former for livscyklusstyring
- Tools, der kan styre livscyklusen for dit produkt
Hvorfor sker RAM-hukommelseslækage på Android?
Problemet er simpelt. Visse objekter bør kun have en fast levetid, og når deres levetid er slut, skal de fjernes.
I teorien bør denne hukommelse bortskaffes, når en proces dræbes ved hjælp af onStop eller onDestroy. Misbrug af objekthenvisningen kan imidlertid forhindre skraldemanden i at deallokere ubrugte objekter. For eksempel: Hvis det ubrugte objekt A refererer til det ubrugte objekt B, vil du ende op med to unødvendige objekter, som garbage collector aldrig vil deallokere, da de refererer til hinanden.
Common Tricks to Stop This Happening
Udviklere kan tage en række skridt for at forhindre døde aktiviteter i at blive fanget i hukommelsen.
- Registrer/afmeld BroadcastReceivers i onResume()/onPause() eller onStart()/onStop()
- Brug ALDRIG statiske variabler til Views/Activities/Contexts
- Singletons, der skal holde referencer til Context, bør bruge applicationContext() eller pakke den ind i WeakReference
- Vær forsigtig med anonyme og ikke-statiske indre klasser, da de har en implicit reference til deres omsluttende klasse.
- Brug statiske indre klasser i stedet for anonyme, hvis de kommer til at overleve den overordnede klasse (som Handlers).
- Hvis den indre eller anonyme klasse kan annulleres (som AsyncTask, Thread, RxSubscriptions), skal den annulleres, når aktiviteten ødelægges.
De Android Lifecycle Aware Components
Når du har gennemført de grundlæggende trin ovenfor, er det tid til noget meget vigtigere: Livscyklusen for appens aktiviteter. Hvis vi ikke administrerer livscyklusen korrekt, ender vi med at hænge på hukommelsen, når der ikke længere er brug for den.
Der er mange forskellige opgaver involveret i dette. For hver aktivitet skal vi afbryde tråde, slippe af med abonnementer i RxJava, annullere AsyncTask-referencer og sikre, at referencen til denne aktivitet (og enhver anden aktivitet, der er forbundet med den) bliver fjernet korrekt. Alle disse opgaver kan dræne en stor del af udviklerens tid.
Det hele bliver endnu mere kompliceret af Model View Presenter (MVP), som er det arkitektoniske mønster, der ofte anvendes til at opbygge brugergrænsefladen i Android. MVP er imidlertid nyttigt til at afkoble forretningslogikken fra View.
I MVP-mønsteret er både View og Presenter abstrakte implementeringer af en kontrakt for en adfærd mellem dem. Den mest almindelige måde at implementere MVP på er at bruge en aktivitet/fragment som en implementering for viewet og en simpel implementering for præsenteren, som er vant til at have en reference til viewet.
Så vi ender med at have et view med en præsentere-reference og en præsentere med en view-reference (Hint: Vi har en potentiel lækage her).
I betragtning af disse potentielle vanskeligheder er det vigtigt, at vi indfører en ordentlig forvaltningsstruktur for at fjerne den overskydende hukommelse, der skabes i løbet af livscyklussen. Der er flere gennemprøvede måder at gøre dette på:
Skabelse af livscyklusbevidste komponenter ved hjælp af Android Arch Lifecycle på Android Studio
Livscyklusbevidste komponenter er smarte. De kan reagere på en ændring i livscyklusstatus for en anden komponent, f.eks. aktiviteter eller fragmenter, ved f.eks. at fjerne hukommelse. Det betyder, at koden bliver lettere og meget mere hukommelseseffektiv.
Arch Lifecycle er et nyt bibliotek fra Android, der tilbyder et sæt værktøjer til at bygge livscyklusbevidste komponenter. Biblioteket fungerer på en abstrakt måde, hvilket betyder, at ejere af livscyklus ikke længere behøver at bekymre sig om at administrere livscyklusen for specifikke opgaver og aktiviteter.
De vigtigste værktøjer og definitioner i Arch Lifecycle er som følger:
- LifeCycle: Et sorteringssystem, der definerer, hvilke objekter der har en Android-livscyklus, og som gør det muligt at overvåge dem derefter.
- LifecycleObserver: En almindelig grænseflade, der overvåger hvert objekt, der er identificeret som havende en Android-livscyklus, ved hjælp af en simpel formel til at håndtere hver vigtig livscyklusbegivenhed.
- @OnLifecycleEvent: En annotation, der kan bruges i klasser, der implementerer LifecycleObserver-grænsefladen. Den giver os mulighed for at indstille vigtige livscyklusbegivenheder, som vil udløse den annoterede metode, når den lanceres. Her er en liste over alle de begivenheder, der kan indstilles:
- ON_ANY
- ON_CREATE
- ON_DESTROY
- ON_PAUSE
- ON_RESUME
- ON_START
- ON_STOP
- LifecycleOwner er som standard implementeret for alle Android-komponenter, hvis livscyklus kan administreres, og giver udvikleren kontrol over hver enkelt begivenhed.
Ved hjælp af disse værktøjer er vi i stand til at sende alle rene opgaver til deres ejere (presenters i vores tilfælde), så vi har en ren, afkoblet kode uden lækager (i det mindste i presenterslaget).
Her er en superbasisk implementering for at vise dig, hvad vi taler om:
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 }
Using Android Arch View Models as Presenters and LiveData
En anden måde at undgå hukommelseslækage gennem fejlstyring af livscyklus er ved at bruge de nye ViewModels fra Arch Components Library.
En ViewModel er en abstrakt klasse, der implementerer en enkelt funktion kendt som onClear, som kaldes automatisk, når et bestemt objekt skal fjernes. ViewModel genereres af rammen og er knyttet til skaberens livscyklus (som en ekstra bonus er det supernemt at injicere med Dagger.)
Selvom at bruge ViewModel, giver LiveData også en vigtig kommunikationskanal. Produktet følger et reaktivt, let at observere mønster, hvilket betyder, at individuelle objekter kan observere det (hvilket skaber mindre koblet kode).
Den store pointe her er, at LiveData kan observeres af en livscyklus-ejer, så dataoverførslen altid styres af livscyklusen -og vi kan sikre, at enhver reference bevares, mens vi bruger dem.
Brug af LeakCanary og Bugfender
Ud over de førnævnte trin ville vi anbefale to vigtige stykker udstyr: LeakCanary, et populært værktøj til overvågning af lækager, og vores helt egen Bugfender.
LeakCanary er et bibliotek til hukommelsesdetektion til Android og Java. Det er open source, så der er et stort fællesskab bag det, og det fortæller dig ikke bare om en lækage – det oplyser dig om den sandsynlige årsag.
Bugfender, vores værktøj til fjernlogning, giver dig mulighed for at debugge individuelle LeakTraces og udvide en klasse kaldet DisplayLeakService, som fortæller os, når en lækage opstår. Så kan vi nemt logge det med Bugfender.
public class LeakUploadService extends DisplayLeakService { override fun afterDefaultHandling(heapDump: HeapDump, result: AnalysisResult, leakInfo: String) { if (result.leakFound) { Bugfender.d("LeakCanary", result.toString()) } }}
Dertil kommer, at brugerne får alle Bugfender’s andre fordele, herunder 24/7 log-optagelse (selv når enheden er offline), indbygget crash-rapportering og en brugervenlig webkonsol, som giver mulighed for at bore ned i individuelle enheder for bedre kundepleje.
For mere om Bugfender kan du klikke her.
Leave a Reply