Context Handling: Context korrekt übergeben und Memory Leaks vermeiden
Der Android Context ist allgegenwärtig – aber falsch verwendet, führt er zu Memory Leaks. Lerne, wann du welchen Context verwenden solltest.
Kaum ein Konzept begegnet dir in Android-Projekten so häufig wie der Context – und kaum eines wird so oft missverstanden. Ein falsch weitergegebener Context kann deine App verlangsamen, abstürzen lassen oder still und heimlich Speicher verschwenden. Wer Android ernsthaft entwickeln will, muss verstehen, was ein Context ist, welche Arten es gibt und wie man ihn sicher durch die eigene Architektur trägt.
Was ist das?
Ein Context ist das Verbindungsstück zwischen deinem Code und der Android-Laufzeitumgebung. Er gibt dir Zugriff auf Ressourcen (Strings, Drawables, Dimensionen), auf System-Services wie den NotificationManager oder ClipboardManager, auf den Dateispeicher und auf viele weitere Android-spezifische APIs. Ohne Context kannst du in Android praktisch nichts tun: keine View inflaten, keine Datenbank öffnen, keinen Intent starten.
Context-Objekte existieren auf zwei zentralen Ebenen:
- Application-Context – lebt so lange wie die gesamte App. Er ist an keine bestimmte Activity oder kein bestimmtes Fragment gebunden.
- Activity-Context (und analog Service-Context) – lebt nur so lange wie die zugehörige Activity-Instanz. Er kennt das aktuelle Theme, die Fenstergröße und andere komponentenspezifische Konfigurationen.
Diese Unterscheidung ist nicht akademisch. Sie ist die Grundlage für alle Entscheidungen darüber, welchen Context du wann übergeben solltest.
Wie funktioniert es?
Die Context-Hierarchie
Context ist eine abstrakte Klasse. ContextWrapper erweitert sie, und davon erben sowohl Application als auch ContextThemeWrapper, von dem wiederum Activity abstammt. Das bedeutet: Jede Activity ist ein Context, aber umgekehrt ist nicht jeder Context eine Activity.
Lebenszyklen als Leitmotiv
Der entscheidende Unterschied liegt im Lebenszyklus:
| Context-Typ | Lebt bis |
|---|---|
applicationContext | App-Prozess endet |
| Activity-Context | Activity wird zerstört (z. B. Rotation, Back-Navigation) |
| Service-Context | Service wird gestoppt |
Wenn ein langlebiges Objekt – etwa ein Repository, ein Singleton oder ein ViewModel – einen Activity-Context hält, bleibt die Activity im Speicher, auch nachdem sie längst zerstört sein sollte. Das System kann den Speicher nicht freigeben: ein klassisches Memory Leak.
Ressourcen laden
Für das Laden von Ressourcen brauchst du in der Regel keinen Activity-Context. applicationContext.getString(R.string.app_name) funktioniert genauso zuverlässig. Einzige Ausnahme: theming-abhängige Ressourcen, etwa Theme-Farben über ?attr/colorPrimary, benötigen den Activity-Context, weil nur er das aktive Theme kennt.
Context in Jetpack und Compose
In Jetpack-Bibliotheken bekommst du den Context häufig über LocalContext.current in Compose oder per Dependency-Injection mit Hilt. ViewModels sollten grundsätzlich keinen direkten Activity-Context halten. Wenn dein ViewModel dennoch einen Context benötigt – zum Beispiel für Ressourcen –, verwende AndroidViewModel. Dieses hält intern einen Application-Context und gibt ihn sicher weiter.
In der Praxis
Das klassische Leak-Muster
// FALSCH: Singleton hält Activity-Context → Memory Leak
object ImageCache {
var context: Context? = null
fun loadDrawable(resId: Int): Drawable? {
return context?.getDrawable(resId)
}
}
// In der Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ImageCache.context = this // diese Activity kann nie freigegeben werden
}
Das Singleton lebt für die gesamte App-Laufzeit. Die Activity, die du hier zuweist, bleibt damit dauerhaft im Speicher – egal wie oft der Nutzer die Activity verlässt oder das Gerät rotiert.
Die sichere Alternative
// RICHTIG: applicationContext übergeben
object ImageCache {
private var appContext: Context? = null
fun init(context: Context) {
appContext = context.applicationContext // nie die Activity selbst
}
fun loadDrawable(resId: Int): Drawable? {
return appContext?.getDrawable(resId)
}
}
// In Application.onCreate():
override fun onCreate() {
super.onCreate()
ImageCache.init(this)
}
Durch context.applicationContext hältst du nur den Application-Context, dessen Lebenszyklus ohnehin mit der App endet. Das Leak verschwindet.
Entscheidungsregel für den Alltag
Frage dich bei jeder Stelle, an der du einen Context übergibst oder speicherst:
Lebt das empfangende Objekt länger als eine Activity-Instanz? Wenn ja →
applicationContext. Wenn nein → Activity-Context nur für kurzlebige, UI-bezogene Operationen.
Typische Stolperfalle: Callbacks und anonyme Klassen
Auch anonyme Klassen und Lambda-Callbacks können Activity-Referenzen halten. Ein Handler oder ein Listener, der in einer Activity erstellt und an ein langlebiges Objekt übergeben wird, kann die Activity genauso leaken wie ein direkter Feldverweis. Nutze stattdessen Kotlin Flows und StateFlow, die sauber mit dem Lifecycle kooperieren und keine versteckten Referenzen erzeugen.
Fazit
Context Handling ist eine der ersten Architekturentscheidungen, die dich als Android-Entwickler täglich begleiten. Die Faustregel ist einfach: Reiche den Application-Context weiter, sobald ein Objekt länger lebt als die Activity, aus der es erzeugt wurde. Prüfe deine Codebase aktiv mit LeakCanary – füge es als Debug-Dependency ein und lass es eine Woche in deiner App laufen. Du wirst schnell sehen, ob bestehender Code an Activity-Referenzen festhält. Suche zusätzlich nach Klassen, die Context-Felder deklarieren, und stelle für jede einzelne die Lebenszyklus-Frage: Wer lebt länger, das Objekt oder die Activity? Diese eine Überprüfung verhindert eine ganze Kategorie von Bugs, bevor sie in der Produktion auftauchen.