Context vs ContextWrapper in Android
What Context really is, how ContextWrapper fits in, and which Context to use to avoid memory leaks.
Context is everywhere in Android: you need it to inflate layouts, start
activities, access resources, and get system services. But “just pass a context”
hides a lot of nuance — and the wrong choice is a classic source of memory leaks.
The Context hierarchy
Context is an abstract class. The concrete implementation that does the real
work is ContextImpl. You rarely touch it directly. Instead you interact with:
Context (abstract)
└── ContextWrapper
├── Application
├── Service
└── ContextThemeWrapper
└── Activity
ContextImpl— the real implementation, created by the framework.ContextWrapper— wraps anotherContextand delegates calls to it.Application,Service,Activity— allContextWrappersubclasses.
What ContextWrapper does
ContextWrapper holds a reference to a base context and forwards every call to
it. This is the decorator pattern: it lets the framework attach extra behavior
without reimplementing all of Context.
// Simplified from the framework
open class ContextWrapper(base: Context) : Context() {
private var mBase: Context = base
override fun getSystemService(name: String): Any? =
mBase.getSystemService(name)
override fun getResources(): Resources =
mBase.getResources()
// ...delegates dozens more methods
}
Activity extends ContextThemeWrapper, which extends ContextWrapper and adds
a theme. That’s why an Activity context can inflate themed views correctly
while the Application context cannot apply your activity theme.
Application vs Activity context
The two you’ll choose between most often:
| Use case | Recommended Context |
|---|---|
| Inflating views / applying a theme | Activity |
Showing a Dialog | Activity |
Starting an Activity | Activity |
| Long-lived singletons / libraries | Application |
WorkManager, Room, DI graph | Application |
The memory leak trap
Holding an Activity context past the activity’s lifetime leaks the entire
activity — and its view tree.
// BAD: a singleton holding an Activity context leaks it forever.
object AnalyticsCache {
var context: Context? = null
}
// In an Activity:
AnalyticsCache.context = this // leaks this Activity!
// GOOD: store the application context for long-lived references.
AnalyticsCache.context = applicationContext
A simple rule
If the object outlives the activity, use
applicationContext. If it needs a theme or shows UI, use theActivitycontext.
Get that distinction right and most “why is my activity not being garbage collected?” questions answer themselves.
Key takeaways
Contextis abstract;ContextImplis the real worker.ContextWrapperdecorates a base context and is the parent ofApplication,Service, and (viaContextThemeWrapper)Activity.- Only
Activitycarries your theme — use it for UI. - Never let a long-lived object hold an
Activitycontext.