Android memóriaszivárgás

How Proper Lifecycle Management Can Prevent Android Memory Leaks

OutOfMemoryException egy gyakori és frusztráló hiba, és az alkalmazás váratlan leállásának egyik fő oka.

“Miért történik ez most, ha az alkalmazás tegnap még tökéletesen működött?”. Ez a kérdés világszerte tanácstalanítja a kezdő és a haladó Android-fejlesztőket egyaránt.

Az OutOfMemory Exceptionsnek számos lehetséges oka van, de az egyik leggyakoribb a memóriaszivárgás – a memória kiosztása az alkalmazásban, amely soha nem kerül felszabadításra. Ez a cikk elmagyarázza, hogyan lehet minimalizálni ezt a kockázatot a hatékony életciklus-kezeléssel – ez a fejlesztési folyamat kulcsfontosságú, de gyakran figyelmen kívül hagyott része.

TL;DR

Ebben a cikkben tárgyaljuk:

  • Az Android memóriaszivárgás okai
  • A megelőzés alapvető lépései
  • Az életciklus-kezelés fejlettebb formái
  • A termék életciklusát kezelő eszközök

Miért fordul elő RAM memóriaszivárgás Androidon?

A probléma egyszerű. Bizonyos objektumoknak csak meghatározott élettartammal kellene rendelkezniük, és amikor a hasznos élettartamuk véget ér, el kell távolítani őket.

Egy folyamat onStop vagy onDestroy használatával történő megölésekor elméletileg el kellene távolítani ezt a memóriát. Az objektumokra való hivatkozással való visszaélés azonban megakadályozhatja a szemétgyűjtőt a nem használt objektumok kiürítésében. Például: ha a nem használt A objektum hivatkozik a nem használt B objektumra, akkor két felesleges objektumot kapunk, amelyeket a szemétgyűjtő soha nem fog kiüríteni, mivel egymásra hivatkoznak.

Közönséges trükkök ennek megakadályozására

A fejlesztők számos lépést tehetnek annak megakadályozására, hogy a halott tevékenységek a memóriában rekedjenek.

  1. Regisztrálja/de-regisztrálja a BroadcastReceivereket az onResume()/onPause() vagy onStart()/onStop() funkcióban
  2. SOHA ne használjon statikus változókat a Views/Activities/Contexts számára
  3. A Singletonok, amelyeknek a Contextre való hivatkozásokat kell tartaniuk, az applicationContext() funkciót használják, vagy csomagolják be WeakReference-be
  4. Legyen óvatos az anonymous és non-statikus belső osztályokkal, mivel ezek implicit hivatkozást tartanak az őket körülvevő osztályra.
    1. A statikus belső osztályokat használjuk anonim helyett, ha azok túlélik a szülő osztályt (mint a Handlers).
    2. Ha a belső vagy anonim osztály törölhető (mint például AsyncTask, Thread, RxSubscriptions), töröljük, amikor az aktivitás megsemmisül.

Az Android életciklusát ismerő komponensek

Amikor a fenti alaplépéseket elvégeztük, itt az ideje valami sokkal fontosabbnak: az alkalmazás tevékenységeinek életciklusának. Ha nem kezeljük megfelelően az életciklust, akkor a végén akkor is lógni fogunk a memórián, amikor már nincs rá szükségünk.

Ezzel kapcsolatban sokféle feladat van. Minden egyes aktivitáshoz meg kell szakítanunk a szálakat, meg kell szabadulnunk az RxJava feliratoktól, meg kell szüntetnünk az AsyncTask hivatkozásokat, és biztosítanunk kell, hogy az adott aktivitás (és minden más, hozzá kapcsolódó aktivitás) hivatkozását megfelelően eltávolítsuk. Mindezek a feladatok rengeteg időt elszívhatnak a fejlesztő idejéből.

A dolgokat még bonyolultabbá teszi a Model View Presenter (MVP), az Androidban a felhasználói felület kialakításához gyakran alkalmazott architektúrális minta. Az MVP azonban hasznos az üzleti logika és a View szétválasztásához.

Az MVP mintában mind a View, mind a Presenter a közöttük lévő viselkedésre vonatkozó szerződés absztrakt megvalósításai. Az MVP megvalósításának leggyakoribb módja, hogy egy Activity/Fragmentet használunk a View implementációjaként, és egy egyszerű implementációt a Presenter számára, aki a View referenciájával szokott rendelkezni.

Így végül is van egy View egy Presenter referenciával és egy Presenter egy View referenciával (Hint: Itt van egy potenciális szivárgás).

Ezek a potenciális nehézségek miatt elengedhetetlen, hogy megfelelő kezelési struktúrát hozzunk létre az életciklus során keletkező felesleges memória eltávolítására. Erre több bevált módszer is létezik:

Élettartam-tudatos komponensek létrehozása az Android Arch Lifecycle használatával az Android Studio-ban

Az élettartam-tudatos komponensek okosak. Képesek reagálni egy másik komponens, például tevékenységek vagy fragmentumok életciklus-állapotának változására, például a memóriától való megszabadulással. Ez azt jelenti, hogy a kód könnyebb és sokkal memóriahatékonyabb.

Az Arch Lifecycle egy új Android könyvtár, amely egy sor eszközt kínál az életciklus-tudatos komponensek készítéséhez. A könyvtár absztrakt módon működik, ami azt jelenti, hogy az életciklus-tulajdonosoknak többé nem kell aggódniuk az egyes feladatok és tevékenységek életciklusának kezelése miatt.

Az Arch Lifecycle legfontosabb eszközei és definíciói a következők:

  • LifeCycle: Egy rendezési rendszer, amely meghatározza, hogy mely objektumok rendelkeznek Android életciklussal, és lehetővé teszi, hogy ezután figyelemmel lehessen kísérni őket.
  • LifecycleObserver: Egy rendszeres interfész, amely minden egyes Android-életciklussal rendelkezőként azonosított objektumot megfigyel, és egy egyszerű képletet használ az egyes kulcsfontosságú életciklus-események kezelésére.
  • @OnLifecycleEvent: LifecycleObserver interfészt megvalósító osztályokban használható megjegyzés. Lehetővé teszi számunkra, hogy meghatározzuk a kulcs életciklus eseményeket, amelyek indításakor az annotált metódust kiváltják. Itt van egy lista a beállítható eseményekről:
    • ON_ANY
    • ON_CREATE
    • ON_DESTROY
    • ON_PAUSE
    • ON_RESUME
    • ON_START
    • .

    • ON_STOP
  • A LifecycleOwner alapértelmezés szerint minden olyan Android komponensnél implementálva van, amelynek életciklusa kezelhető, és a fejlesztőnek ad irányítást minden egyes esemény felett.

Ezeket az eszközöket használva minden tiszta feladatot el tudunk küldeni a tulajdonosuknak (esetünkben az előadóknak), így egy tiszta, decoupled kódot kapunk, amely mentes a szivárgásoktól (legalábbis az előadók rétegében).

Itt van egy szuper-alap megvalósítás, hogy megmutassuk, miről beszélünk:

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 }

Az Android Arch View Models használata Presenterként és LiveData

Egy másik módja az életciklus rossz kezelése miatti memóriaszivárgás elkerülésének az Arch Components Library új ViewModels használata.

A ViewModel egy absztrakt osztály, amely egyetlen onClear nevű függvényt valósít meg, amely automatikusan meghívódik, amikor egy adott objektumot el kell távolítani. A ViewModelt a keretrendszer generálja, és az alkotó életciklusához van csatolva (plusz bónuszként szuperegyszerű a Daggerrel való injektálása.)

A ViewModel használata mellett a LiveData egy létfontosságú kommunikációs csatornát is biztosít. A termék egy reaktív, könnyen megfigyelhető mintát követ, ami azt jelenti, hogy az egyes objektumok megfigyelhetik azt (kevésbé csatolt kódot létrehozva).

A nagyszerű pont itt az, hogy a LiveData megfigyelhető az életciklus tulajdonosa által, így az adatátvitelt mindig az életciklus kezeli – és biztosíthatjuk, hogy minden hivatkozás megmarad a használatuk során.

A LeakCanary és a Bugfender használata

A fent említett lépéseken kívül két fontos eszközt szerettünk volna még ajánlani: A LeakCanary-t, egy népszerű eszközt a szivárgások megfigyelésére, és a saját Bugfenderünket.

A LeakCanary egy memóriaérzékelő könyvtár Androidhoz és Java-hoz. Nyílt forráskódú, így hatalmas közösség áll mögötte, és nem egyszerűen csak szól a szivárgásról – tájékoztat a valószínű okáról is.

A Bugfender, a távoli naplózó eszközünk lehetővé teszi az egyes LeakTraces-ek hibakeresését és a DisplayLeakService nevű osztály kiterjesztését, amely tudatja velünk, ha szivárgás keletkezik. Ezután a Bugfenderrel könnyedén naplózhatjuk azt.

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

Ezeken felül a felhasználók megkapják a Bugfender összes többi előnyét, beleértve a 24/7 naplófelvételt (még akkor is, ha az eszköz offline állapotban van), a beépített hibajelentést és egy könnyen használható webes konzolt, amely lehetővé teszi az egyes eszközökre való fúrást a jobb ügyfélszolgálat érdekében.

A Bugfenderről bővebben itt olvashat.

Kattintson ide.

Leave a Reply