Android minnesläckage

Hur korrekt livscykelhantering kan förhindra Android minnesläckage

OutOfMemoryException är en vanlig och frustrerande bugg och en av huvudorsakerna till oväntad avstängning av en app.

”Varför händer det här nu, om appen fungerade perfekt igår?” Det är en fråga som förbryllar både nybörjare och avancerade Android-utvecklare världen över.

Det finns en mängd olika potentiella orsaker till OutOfMemory Exceptions, men en av de vanligaste är minnesläckage – allokering av minne i appen som aldrig frigörs. I den här artikeln förklaras hur man minimerar denna risk genom effektiv livscykelhantering – en viktig men ofta förbisedd del av utvecklingsprocessen.

TL;DR

I den här artikeln diskuterar vi:

  • Grunderna för minnesläckage i Android
  • Grundläggande steg för att förhindra det
  • Mer avancerade former av livscykelhantering
  • Varormer som kan hantera livscykeln för din produkt

Varför uppstår RAM-minnesläckage i Android?

Problemet är enkelt. Vissa objekt ska bara ha en bestämd livslängd, och när deras livslängd är slut måste de tas bort.

I teorin ska detta minne tas bort när en process dödas med onStop eller onDestroy. Missbruk av objektreferensering kan dock hindra sophämtaren från att avallokera oanvända objekt. Till exempel: Om det oanvända objektet A refererar till det oanvända objektet B får du två onödiga objekt som sophämtaren aldrig kommer att avallokera eftersom de refererar till varandra.

Gemensamma knep för att förhindra att det här händer

Utvecklare kan vidta ett antal åtgärder för att förhindra att döda aktiviteter fastnar i minnet.

  1. Registrera/avregistrera BroadcastReceivers i onResume()/onPause() eller onStart()/onStop()
  2. Använd ALDRIG statiska variabler för vyer/aktiviteter/kontexter
  3. Singletons som behöver hålla referenser till kontexten bör använda applicationContext() eller linda in den i WeakReference
  4. Var försiktig med anonyma och icke-statiska inre klasser eftersom de har en implicit referens till sin omslutande klass.
    1. Använd statiska inre klasser i stället för anonyma om de kommer att överleva den överordnade klassen (som Handlers).
    2. Om den inre eller anonyma klassen kan avbrytas (t.ex. AsyncTask, Thread, RxSubscriptions), avbryt den när aktiviteten förstörs.

De Android Lifecycle Aware Components

När du har genomfört de grundläggande stegen ovan är det dags för något mycket viktigare: Livscykeln för appens aktiviteter. Om vi inte hanterar livscykeln korrekt kommer det att sluta med att vi hänger kvar i minnet när det inte längre behövs.

Det finns många olika uppgifter som är inblandade i detta. För varje aktivitet måste vi avbryta trådar, göra oss av med prenumerationer i RxJava, avbryta AsyncTask-referenser och se till att referensen till den här aktiviteten (och alla andra aktiviteter som är kopplade till den) tas bort korrekt. Alla dessa uppgifter kan ta en stor del av utvecklarens tid i anspråk.

Det hela blir ännu mer komplicerat med Model View Presenter (MVP), det arkitekturmönster som ofta används för att bygga användargränssnittet i Android. MVP är dock användbart för att frikoppla affärslogiken från vyn.

I MVP-mönstret är både vyn och presentatören abstrakta implementationer av ett kontrakt för ett beteende mellan dem. Det vanligaste sättet att implementera MVP är att använda en aktivitet/fragment som en implementering för vyn och en enkel implementering för presentatören som är van vid att ha en referens till vyn.

Det slutar med att vi har en vy med en presentatörsreferens och en presentatör med en vynsreferens (tips: Vi har en potentiell läcka här).

Med tanke på dessa potentiella svårigheter är det viktigt att vi inför en ordentlig förvaltningsstruktur för att ta bort det överskottsminne som skapas under livscykeln. Det finns flera beprövade sätt att göra detta:

Skapa livscykelmedvetna komponenter med hjälp av Android Arch Lifecycle i Android Studio

Livscykelmedvetna komponenter är smarta. De kan reagera på en förändring i livscykelstatusen för en annan komponent, t.ex. aktiviteter eller fragment, genom att t.ex. göra sig av med minne. Detta innebär att koden blir lättare och mycket mer minneseffektiv.

Arch Lifecycle är ett nytt bibliotek från Android som erbjuder en uppsättning verktyg för att bygga livscykelmedvetna komponenter. Biblioteket arbetar på ett abstrakt sätt, vilket innebär att livscykelägare inte längre behöver oroa sig för att hantera livscykeln för specifika uppgifter och aktiviteter.

De viktigaste verktygen och definitionerna i Arch Lifecycle är följande:

  • LifeCycle: Ett sorteringssystem som definierar vilka objekt som har en Android-livscykel och gör det möjligt att sedan övervaka dem.
  • LifecycleObserver: Ett vanligt gränssnitt som övervakar varje objekt som har en Android-livscykel och använder en enkel formel för att hantera varje viktig livscykelhändelse.
  • @OnLifecycleEvent: En annotation som kan användas i klasser som implementerar gränssnittet LifecycleObserver. Den gör det möjligt att ställa in viktiga livscykelhändelser som kommer att utlösa den annoterade metoden när den startas. Här finns en lista över alla händelser som kan ställas in:
    • ON_ANY
    • ON_CREATE
    • ON_DESTROY
    • ON_PAUSE
    • ON_RESUME
    • ON_START
    • ON_STOP
  • LifecycleOwner implementeras som standard för varje Android-komponent vars livscykel kan hanteras, och ger utvecklaren kontroll över varje händelse.

Med hjälp av dessa verktyg kan vi skicka alla rena uppgifter till sina ägare (presentatörer i vårt fall), så att vi har en ren, frikopplad kod fri från läckor (åtminstone i presentatörslagret).

Här är en superbasisk implementering för att visa dig vad vi pratar 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 }

Användning av Android Arch View Models som presentatörer och LiveData

Ett annat sätt att undvika minnesläckage genom felhantering av livscykeln är att använda de nya ViewModels från Arch Components Library.

En ViewModel är en abstrakt klass som implementerar en enda funktion kallad onClear, som anropas automatiskt när ett visst objekt måste tas bort. ViewModel genereras av ramverket och är knuten till skaparens livscykel (som en extra bonus är det superenkelt att injicera med Dagger.)

Som att använda ViewModel tillhandahåller LiveData också en viktig kommunikationskanal. Produkten följer ett reaktivt, lättobserverat mönster, vilket innebär att enskilda objekt kan observera den (vilket skapar mindre kopplad kod).

Den stora poängen här är att LiveData kan observeras av en livscykelägare, så dataöverföringen hanteras alltid av livscykeln – och vi kan se till att alla referenser behålls när vi använder dem.

Användning av LeakCanary och Bugfender

Inom de ovan nämnda stegen ville vi rekommendera två viktiga delar av utrustningen: LeakCanary, ett populärt verktyg för övervakning av läckor, och vår egen Bugfender.

LeakCanary är ett bibliotek för minnesdetektering för Android och Java. Det är öppen källkod, så det finns en stor gemenskap bakom det, och det berättar inte bara om en läcka – det informerar dig om den troliga orsaken.

Bugfender, vårt fjärrloggningsverktyg, gör det möjligt för dig att felsöka enskilda LeakTraces och utöka en klass som heter DisplayLeakService, som låter oss veta när en läcka uppstår. Då kan vi enkelt logga den med Bugfender.

public class LeakUploadService extends DisplayLeakService { override fun afterDefaultHandling(heapDump: HeapDump, result: AnalysisResult, leakInfo: String) { if (result.leakFound) { Bugfender.d("LeakCanary", result.toString()) } }}

Därutöver får användarna alla Bugfengers andra fördelar, inklusive loggregistrering dygnet runt (även när enheten är offline), inbyggd kraschrapportering och en användarvänlig webbkonsol som gör det möjligt att borra ner i enskilda enheter för att få bättre kundvård.

För mer information om Bugfender, vänligen klicka här.

Leave a Reply