Únik paměti v systému Android

Jak lze správnou správou životního cyklu zabránit únikům paměti v systému Android

OutOfMemoryException je častá a frustrující chyba a jedna z hlavních příčin neočekávaného ukončení aplikace.

„Proč se to děje právě teď, když aplikace ještě včera fungovala perfektně?“ Je to otázka, která mate začátečníky i pokročilé vývojáře systému Android po celém světě.

Příčin výjimek OutOfMemory je celá řada, ale jednou z nejčastějších je únik paměti – alokace paměti v aplikaci, která není nikdy uvolněna. V tomto článku se dozvíte, jak toto riziko minimalizovat pomocí efektivní správy životního cyklu – důležité, ale často opomíjené součásti procesu vývoje.

TL;DR

V tomto článku probereme:

  • Důvody úniku paměti v systému Android
  • Základní kroky, jak mu předcházet
  • Pokročilejší formy správy životního cyklu
  • Nástroje, které mohou spravovat životní cyklus vašeho produktu

Proč dochází k úniku paměti RAM v systému Android?

Problém je jednoduchý. Některé objekty by měly mít pouze pevně stanovenou životnost, a když jejich životnost skončí, je třeba je odstranit.

Teoreticky by tato paměť měla být zlikvidována při zabití procesu pomocí funkce onStop nebo onDestroy. Zneužití odkazování na objekty však může zabránit tomu, aby garbage collector nepoužívané objekty dealokoval. Například: pokud nepoužívaný objekt A odkazuje na nepoužívaný objekt B, vzniknou dva nepotřebné objekty, které garbage collector nikdy nerozdělí, protože odkazují jeden na druhý.

Obvyklé triky, jak tomu zabránit

Vývojáři mohou podniknout řadu kroků, aby zabránili uvíznutí mrtvých aktivit v paměti.

  1. Zaregistrujte/odregistrujte BroadcastReceiver v onResume()/onPause() nebo onStart()/onStop()
  2. NIKDY nepoužívejte statické proměnné pro pohledy/aktivity/kontexty
  3. Singletony, které potřebují držet odkazy na kontext, by měly používat applicationContext() nebo jej zabalit do WeakReference
  4. Buďte opatrní s anonymními a neanonymními proměnnými.statickými vnitřními třídami, protože ty drží implicitní odkaz na svou obklopující třídu.
    1. Používejte statické vnitřní třídy místo anonymních, pokud mají přežít nadřazenou třídu (jako Handlers).
    2. Pokud je vnitřní nebo anonymní třída zrušitelná (jako AsyncTask, Thread, RxSubscriptions), zrušte ji při zničení aktivity.

Komponenty, které si uvědomují životní cyklus systému Android

Pokud jste dokončili výše uvedené základní kroky, je čas na něco mnohem důležitějšího: Životní cyklus aktivit aplikace. Pokud nebudeme správně spravovat životní cyklus, skončí to tak, že budeme viset na paměti, i když už ji nebudeme potřebovat.

S tím souvisí mnoho různých úkolů. Pro každou aktivitu musíme přerušit vlákna, zbavit se odběrů v RxJava, zrušit odkazy na AsyncTask a zajistit správné odstranění odkazu na tuto aktivitu (a všechny ostatní aktivity, které jsou k ní připojeny). Všechny tyto úkoly mohou vývojáře připravit o obrovské množství času.

Situaci ještě více komplikuje Model View Presenter (MVP), architektonický vzor často používaný při vytváření uživatelského rozhraní v systému Android. MVP je však užitečný k oddělení obchodní logiky od View.

Ve vzoru MVP jsou View i Presenter abstraktní implementací smlouvy o chování mezi nimi. Nejběžnějším způsobem implementace MVP je použití Aktivity/Fragmentu jako implementace pro View a jednoduché implementace pro Presenter, který je zvyklý mít odkaz na View.

Takže nakonec máme View s odkazem na Presenter a Presenter s odkazem na View (Nápověda: Máme zde potenciální únik informací).

Vzhledem k těmto potenciálním potížím je nezbytné, abychom zavedli vhodnou strukturu správy, která odstraní přebytečnou paměť vytvořenou během životního cyklu. Existuje několik osvědčených způsobů, jak toho dosáhnout:

Vytváření komponent s ohledem na životní cyklus pomocí Android Arch Lifecycle v aplikaci Android Studio

Komponenty s ohledem na životní cyklus jsou chytré. Mohou reagovat na změnu stavu životního cyklu jiné komponenty, jako jsou aktivity nebo fragmenty, například tím, že se zbaví paměti. To znamená, že kód je lehčí a mnohem úspornější na paměť.

Arch Lifecycle je nová knihovna od společnosti Android, která nabízí sadu nástrojů pro vytváření komponent s vědomím životního cyklu. Knihovna pracuje abstraktním způsobem, což znamená, že vlastníci životního cyklu se již nemusí starat o správu životního cyklu konkrétních úloh a činností.

Klíčové nástroje a definice Arch Lifecycle jsou následující:

  • LifeCycle:
  • LifecycleObserver: Systém třídění, který definuje, které objekty mají životní cyklus systému Android, a umožňuje jejich následné sledování:
  • @OnLifecycleEvent: Pravidelné rozhraní, které sleduje každý objekt identifikovaný jako objekt s životním cyklem systému Android a používá jednoduchý vzorec pro zpracování každé klíčové události životního cyklu: Anotace, kterou lze použít ve třídách implementujících rozhraní LifecycleObserver. Umožňuje nám nastavit klíčové události životního cyklu, které při každém spuštění spustí anotovanou metodu. Zde je seznam všech událostí, které lze nastavit:
    • ON_ANY
    • ON_CREATE
    • ON_DESTROY
    • ON_PAUSE
    • ON_RESUME
    • ON_START
    • ON_STOP
  • LifecycleOwner je implicitně implementován pro každou komponentu systému Android, jejíž životní cyklus lze spravovat, a dává vývojáři kontrolu nad každou událostí.

Pomocí těchto nástrojů jsme schopni poslat všechny čisté úlohy jejich vlastníkům (v našem případě prezentérům), takže máme čistý, oddělený kód bez úniků (alespoň ve vrstvě prezentéru).

Tady je superzákladní implementace, která vám ukáže, o čem mluvíme:

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 }

Použití modelů zobrazení Arch Androidu jako prezentérů a LiveData

Dalším způsobem, jak se vyhnout únikům paměti kvůli špatné správě životního cyklu, je použití nových modelů zobrazení z knihovny komponent Arch.

ViewModel je abstraktní třída, která implementuje jedinou funkci známou jako onClear, která je automaticky volána, když má být konkrétní objekt odstraněn. ViewModel je generován frameworkem a je připojen k životnímu cyklu tvůrce (jako bonus jej lze velmi snadno injektovat pomocí Daggeru.)

Kromě použití ViewModelu poskytuje LiveData také důležitý komunikační kanál. Řídí se reaktivním, snadno pozorovatelným vzorem, což znamená, že jej mohou pozorovat jednotlivé objekty (čímž vzniká méně provázaný kód).

Skvělé je, že LiveData může pozorovat vlastník životního cyklu, takže přenos dat je vždy řízen životním cyklem -a my můžeme zajistit, že při jejich použití bude zachován jakýkoli odkaz.

Použití nástrojů LeakCanary a Bugfender

Kromě výše uvedených kroků jsme chtěli doporučit dvě důležité pomůcky: LeakCanary, populární nástroj pro sledování úniků paměti, a náš vlastní Bugfender.

LeakCanary je knihovna pro detekci úniků paměti pro Android a Javu. Je open-source, takže za ní stojí obrovská komunita, a o úniku vás nejen informuje – informuje vás i o jeho pravděpodobné příčině.

Bugfender, náš nástroj pro vzdálené logování, umožňuje ladit jednotlivé LeakTraces a rozšiřuje třídu DisplayLeakService, která nám dává vědět, když je únik vyvolán. Pak jej můžeme pomocí Bugfenderu snadno zaznamenat.

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

Uživatelé navíc získají všechny další výhody Bugfenderu, včetně nepřetržitého zaznamenávání protokolů (i když je zařízení offline), integrovaného hlášení o haváriích a snadno použitelné webové konzole, která umožňuje proniknout do jednotlivých zařízení pro lepší péči o zákazníky.

Více informací o Bugfenderu najdete zde.

Další informace o Bugfenderu naleznete zde.

Leave a Reply