Skip to content

@ApplicationContext vs Activity Context: When to Use Each in Hilt

The Problem

I was reviewing an Android developer quiz on Reddit when a comment by Zhuinden caught my attention. He was discussing a potential quiz question about context injection in Hilt, specifically for image loading with Glide.

His point was sharp: “Glide uses Glide.with(activity) and Glide.with(fragment) to scope the image loading request, you can’t just pass it a Context as is, especially not an application context.”

This highlights a common confusion I see in Android development. When Hilt asks you to inject a Context, you have choices: @ApplicationContext, @ActivityContext, or just inject Activity directly. The wrong choice doesn’t just cause build errors—it causes memory leaks.

Why Context Choice Matters

In Android, Context is not a single thing. You have:

Context Hierarchy in Android
Context
|
+-- Application Context (lives for app lifetime)
|
+-- Activity Context (lives for activity lifetime)
|
+-- Fragment Context (lives for fragment lifetime)

The key difference is lifecycle. Application context survives until your app is killed. Activity context dies when the activity is destroyed. When you inject the wrong context into a long-lived object, you create a memory leak.

Context hierarchy
flowchart TD
A[Context Types] --> B[Application Context]
A --> C[Activity Context]
B --> D[Lifecycle: App lifetime]
B --> E[Use for: Singletons, Database, SharedPreferences]
C --> F[Lifecycle: Activity lifetime]
C --> G[Use for: UI operations, Glide, LayoutInflater]
D --> H[Memory-safe in singletons]
F --> I[DANGEROUS in singletons - memory leak!]
style I fill:#ffcccc

When to Use @ApplicationContext

Use @ApplicationContext when your dependency needs to survive across the entire app lifecycle. This is the safe choice for singletons because application context won’t be garbage collected while your app is running.

SharedPreferences

SharedPreferences with @ApplicationContext
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideSharedPreferences(
@ApplicationContext context: Context
): SharedPreferences {
return context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
}
}

SharedPreferences files belong to the application, not any specific activity. Using application context here is correct.

Room Database

Room Database with @ApplicationContext
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Singleton
fun provideDatabase(
@ApplicationContext context: Context
): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database"
).build()
}
}

Database files persist across activities. The database singleton should live as long as the app, so application context is the right choice.

Analytics and Logging Services

Analytics Service with @ApplicationContext
@Singleton
class AnalyticsService @Inject constructor(
@ApplicationContext private val context: Context
) {
fun trackEvent(name: String, params: Map<String, Any>) {
// Analytics needs app-wide context for device info
// It tracks events across all activities
}
}

Analytics services track user behavior across the entire app lifecycle. They shouldn’t be tied to any specific activity.

When to Use Activity Context

Use activity context when your dependency is tied to UI operations or needs lifecycle awareness. This is critical for:

  1. Operations that should stop when the activity dies
  2. APIs that require activity-specific behavior
  3. Resources that depend on the current activity theme

The Glide Case: Why Context Matters

This is where many developers go wrong. Glide explicitly requires activity or fragment context for proper lifecycle management:

Glide with Activity Context
@ActivityScoped
class ImageLoader @Inject constructor(
private val activity: Activity // Hilt provides this automatically
) {
fun loadImage(url: String, imageView: ImageView) {
Glide.with(activity) // Proper lifecycle scoping!
.load(url)
.into(imageView)
}
}

Why does Glide need activity context? Because Glide pauses loading when the activity pauses, and cancels requests when the activity is destroyed. If you pass application context, Glide can’t manage the lifecycle properly:

WRONG: Glide with ApplicationContext
@Module
@InstallIn(SingletonComponent::class)
object WrongModule {
@Provides
@Singleton
fun provideImageLoader(
@ApplicationContext context: Context // DANGER!
): ImageLoader {
// This will load images even after activities are destroyed
// causing memory leaks and wrong lifecycle behavior
return GlideImageLoader(context)
}
}

With application context, Glide has no way to know when to pause or cancel requests. Images might load into destroyed activities, causing crashes and memory leaks.

Resource Access with Theme

Resource Helper with Activity Context
@ActivityScoped
class ResourceHelper @Inject constructor(
@ActivityContext private val context: Context
) {
fun getString(resId: Int): String = context.getString(resId)
fun getDrawable(resId: Int): Drawable? {
// Activity context respects the current theme
return ContextCompat.getDrawable(context, resId)
}
fun getThemedColor(attrResId: Int): Int {
val typedValue = TypedValue()
context.theme.resolveAttribute(attrResId, typedValue, true)
return typedValue.data
}
}

Activity context respects the activity’s theme. Application context uses the application theme, which might differ. If your activities use different themes, you need activity context for correct resource resolution.

Three Ways to Inject Context in Hilt

Hilt gives you three options for context injection:

Context Injection Options
class ExampleClass @Inject constructor(
@ApplicationContext private val appContext: Context, // Application context
@ActivityContext private val activityContext: Context, // Activity context
private val activity: Activity // Activity directly (activity-scoped only)
) {
fun demonstrateUsage() {
// appContext: Use for SharedPreferences, Database, Analytics
// activityContext: Use for Resources, LayoutInflater, theme-aware operations
// activity: Use when you need Activity-specific APIs (startActivityForResult, etc.)
}
}

Option 1: @ApplicationContext

Available in any scope. Use for app-wide singletons.

@ApplicationContext Usage
@Singleton
class AppDatabase @Inject constructor(
@ApplicationContext private val context: Context
) {
// Lives as long as the app
}

Option 2: @ActivityContext

Only available in ActivityComponent or ActivityRetainedComponent. Use for activity-scoped dependencies.

@ActivityContext Usage
@ActivityScoped
class ActivityResourceProvider @Inject constructor(
@ActivityContext private val context: Context
) {
// Lives as long as the activity
}

Option 3: Inject Activity Directly

Only available in ActivityComponent. Use when you need Activity-specific methods.

Direct Activity Injection
@ActivityScoped
class Navigator @Inject constructor(
private val activity: Activity
) {
fun startActivityForResult(intent: Intent, requestCode: Int) {
activity.startActivityForResult(intent, requestCode)
}
fun finish() {
activity.finish()
}
}

Scoping Implications

The context you inject must match your dependency’s scope:

Context and Scope Compatibility
┌─────────────────────────────────────────────────────────────────┐
│ Scope │ Allowed Context Types │
├─────────────────────────────────────────────────────────────────┤
│ SingletonComponent │ @ApplicationContext only │
│ ActivityRetainedComp │ @ApplicationContext, @ActivityContext │
│ ActivityComponent │ @ApplicationContext, @ActivityContext, │
│ │ Activity (direct) │
│ FragmentComponent │ Same as ActivityComponent │
└─────────────────────────────────────────────────────────────────┘

Singleton with Activity Context = Memory Leak

Memory Leak Pattern
// WRONG: Singleton holding activity context
@Singleton // Lives forever
class BadSingleton @Inject constructor(
@ActivityContext private val context: Context // Activity dies, but singleton holds reference
) {
// Memory leak! Activity can't be garbage collected
}

When the activity is destroyed, the singleton still holds a reference to it. The activity can’t be garbage collected until the app is killed.

Activity-Scoped with Application Context = OK

Safe Pattern
// OK: Activity-scoped with application context
@ActivityScoped
class ActivityRepository @Inject constructor(
@ApplicationContext private val context: Context // Application context outlives activity
) {
// No leak - application context lives as long as app
}

This is safe because application context lives longer than the activity. No memory leak occurs.

Practical Decision Guide

Here’s a simple decision tree:

Context Selection Guide
Need a Context?
├─ Is the dependency a Singleton?
│ └─ YES → Must use @ApplicationContext
├─ Does the dependency need lifecycle awareness?
│ ├─ YES → Use @ActivityContext or Activity
│ │
│ └─ NO → Use @ApplicationContext (simpler)
├─ Does it need Activity-specific APIs?
│ └─ YES → Inject Activity directly
└─ Does it need theme-aware resources?
└─ YES → Use @ActivityContext

Quick Reference Table

Dependency TypeRecommended ContextReason
SharedPreferences@ApplicationContextApp-wide storage
Room Database@ApplicationContextSingleton scope
Retrofit/OkHttp@ApplicationContextSingleton scope
Analytics@ApplicationContextApp-wide tracking
Glide/ImageLoader@ActivityContext or ActivityLifecycle awareness
LayoutInflater@ActivityContextTheme-aware inflation
Resource resolver@ActivityContextTheme-aware resources
NavigatorActivityActivity-specific APIs

Compose Considerations

In Compose, you typically don’t inject context via Hilt. Instead, use LocalContext.current:

Context in Compose
@Composable
fun ProfileImage(
imageUrl: String,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
// Coil example - automatically lifecycle-aware
AsyncImage(
model = imageUrl,
contentDescription = null,
modifier = modifier
)
}
@Composable
fun rememberImageLoader(): ImageLoader {
val context = LocalContext.current
return remember(context) {
ImageLoader.Builder(context)
.components {
// Configure components
}
.build()
}
}

Compose’s LocalContext.current provides the appropriate context based on the composition’s lifecycle. For image loading in Compose, Coil handles lifecycle automatically.

Common Mistakes

Mistake 1: Singleton with @ActivityContext

Memory Leak: Singleton with Activity Context
// WRONG - Won't even compile
@Singleton
class BadAnalytics @Inject constructor(
@ActivityContext private val context: Context // Error!
) { }
// Hilt prevents this at compile time with:
// "android.app.Activity cannot be provided in SingletonComponent"

Mistake 2: Using ApplicationContext for Glide

Lifecycle Bug: Glide with Wrong Context
// PROBLEMATIC - Compiles but causes issues
@Singleton
class ImageManager @Inject constructor(
@ApplicationContext private val context: Context
) {
fun load(url: String, view: ImageView) {
Glide.with(context) // No lifecycle management!
.load(url)
.into(view)
}
}

This compiles but Glide can’t manage the request lifecycle. If the activity is destroyed, the image might still load into a dead view.

Mistake 3: Injecting Context Where It’s Not Needed

Unnecessary Context Injection
// UNNECESSARY - Context not actually used
class UserRepository @Inject constructor(
@ApplicationContext private val context: Context, // Not used!
private val api: UserApi
) {
suspend fun getUsers() = api.getUsers() // No context usage
}

Only inject context when you actually need it. Every injected dependency adds complexity.

Summary

Context injection in Hilt is not a random choice. It has real implications for memory management and lifecycle behavior:

  • @ApplicationContext is for singleton-scoped dependencies and app-wide services that don’t need lifecycle awareness
  • @ActivityContext is for activity-scoped dependencies that need theme-aware resources or lifecycle binding
  • Activity injection is for when you need Activity-specific APIs like startActivityForResult

The Glide example from Reddit perfectly illustrates why this matters. Using @ApplicationContext for image loading breaks lifecycle management, leading to memory leaks and crashes.

When in doubt, ask yourself: “What is the scope of this dependency, and does it need to know when the activity dies?” The answer tells you which context to inject.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments