Context 在 Android 中被描述为 “关于应用环境的全局信息接口”。它贯穿整个 Android 框架,是访问系统资源、启动组件、获取系统服务的入口。然而,Context 的继承层级、不同类型的 Context 之间的差异、以及使用不当可能导致的内存泄漏,是深入理解 Android 框架不可或缺的知识。本文深入分析 Context 的继承体系、ContextImpl 的创建过程以及各类型 Context 的差异。
一、Context 的继承体系
1.1 类图
abstract Context |
从 ContextImpl 角度更准确地表达:
Context (abstract) |
AOSP 路径:
frameworks/base/core/java/android/content/Context.javaframeworks/base/core/java/android/content/ContextWrapper.javaframeworks/base/core/java/android/app/ContextImpl.javaframeworks/base/core/java/android/view/ContextThemeWrapper.java
1.2 ContextWrapper——装饰器模式的经典应用
ContextWrapper 是装饰器模式的典型实现,它持有一个 Context 引用(mBase),所有方法默认委托给 mBase:
// frameworks/base/core/java/android/content/ContextWrapper.java |
这一设计的精妙之处在于:Activity、Service、Application 这些类都是 ContextWrapper 的子类,但它们内部的 mBase 指向的是同一个 ContextImpl 类型。也就是说,真正干活的是 ContextImpl,而 ContextWrapper 及其子类只是提供了不同的”视角”(比如 Activity 多了主题支持)。
装饰器模式在此处的价值:可以嵌套多个 ContextWrapper。例如,Activity 的 Context 层级:Activity(ContextThemeWrapper) → ContextWrapper(mBase = ContextImpl)。ContextThemeWrapper 拦截了 getTheme()、getDrawable() 等资源相关方法,注入了 Activity 特定主题,而其他方法(如 startService)则直接委托给 ContextImpl。
二、ContextImpl——Context 的真正实现
2.1 ContextImpl 的核心字段
// frameworks/base/core/java/android/app/ContextImpl.java |
mActivityToken 是区分 Application Context 和 Activity Context 的关键字段。Application Context 的 mActivityToken 为 null,而 Activity Context 持有 ActivityRecord 的 token。这个 token 在 startActivity、getSystemService(WINDOW_SERVICE) 等需要窗口令牌的操作中起关键作用。
mOpPackageName 与 mBasePackageName 的区别:当 App A 通过 createPackageContext("com.example.b", ...) 创建 App B 的 Context 时,mBasePackageName = “com.example.b”(目标包),mOpPackageName = “com.example.a”(当前操作包)。系统在权限检查时使用 mOpPackageName 来确定调用者身份。
2.2 ContextImpl 的创建
不同类型的 Context 有不同的创建方式,但都通过 ContextImpl 的静态工厂方法创建:
// frameworks/base/core/java/android/app/ContextImpl.java |
2.3 ActivityThread 中创建 Context
// frameworks/base/core/java/android/app/ActivityThread.java |
三、不同类型 Context 的差异
3.1 Application Context vs Activity Context
这是最容易混淆的一组概念:
| 特性 | Application Context | Activity Context |
|---|---|---|
| 生命周期 | 伴随整个应用进程 | 伴随 Activity 生命周期 |
| 主题支持 | 无(不是 ContextThemeWrapper) | 有(继承 ContextThemeWrapper) |
| startActivity | 需要 NEW_TASK flag | 不需要(默认放入当前 Task) |
| inflate layout | 使用默认主题 | 使用 Activity 主题 |
| 显示 Dialog | 不允许(需要 Window token) | 允许 |
| getSystemService(WINDOW_SERVICE) | 返回全局 WindowManager | 返回与 Activity 关联的 WindowManager |
3.2 getSystemService 的实现
Context 的 getSystemService 是获取系统服务的统一入口:
// frameworks/base/core/java/android/app/ContextImpl.java |
CachedServiceFetcher 维护一个 @GuardedBy 的缓存,首次调用时通过 createService 创建服务对象,后续调用返回缓存。缓存 key 是 Context 实例——这意味着不同的 ContextImpl 返回不同的服务实例。
ctx.getOuterContext() 返回 ContextWrapper 链中最外层的 Context。例如 Activity 的场景中,ContextImpl → ContextThemeWrapper → Activity,getOuterContext() 返回的是 Activity 实例。
3.3 Service 的 Context
// frameworks/base/core/java/android/app/Service.java |
Service 直接继承 ContextWrapper(不是 ContextThemeWrapper),因此它没有主题支持,这也就是为什么你不能在 Service 中 inflate layout 并使用 Activity 主题。
3.4 BroadcastReceiver 的 Context
// BroadcastReceiver.onReceive(Context context, Intent intent) |
onReceive 中传入的 Context 是 ReceiverRestrictedContext(ContextWrapper 的子类)。这个 Context 不能调用 registerReceiver 和 bindService——因为 Receiver 的生命周期短(onReceive 执行完后的几秒内就可能被系统销毁),注册的 receiver 和绑定的 service 会泄漏。
3.5 ContentProvider 的 Context
ContentProvider 在 onCreate() 被调用前通过 attachInfo() 方法绑定 Context。它同样继承 ContextWrapper,生命周期与 Application 相同:
// frameworks/base/core/java/android/content/ContentProvider.java |
四、Context 的内存泄漏分析
4.1 常见泄漏场景
场景1:静态变量持有 Activity Context
public class MyManager { |
解法:使用 context.getApplicationContext() 或让静态变量持有 Application Context。
场景2:单例持有 Activity 引用:如 EventBus 注册了 Activity 但未解注册。
场景3:内部类/匿名类隐式持有外部类引用:如 Handler、AsyncTask 作为 Activity 的非静态内部类,它们持有对外部 Activity 的隐式引用。如果 Handler 消息延迟较长,Activity 在消息执行前被销毁,就会泄漏。
场景4:Toast 泄漏
// 错误:使用 Activity Context 创建 Toast |
如果 Activity 销毁时 Toast 尚未消失,Toast 的内部 TN binder 持有 Activity 的 Context 引用,导致泄漏。推荐使用 getApplicationContext()。
五、Context 在资源加载中的角色
ContextImpl 持有 Resources 对象,负责:
- 资源加载:
getResources().getString(R.string.xxx)、getResources().getDrawable(R.drawable.xxx) - AssetManager:管理 APK 内的 assets 目录文件
- 数据库:
openOrCreateDatabase()创建 SQLiteDatabase - 文件操作:
openFileInput()、openFileOutput()等 - SharedPreferences:
getSharedPreferences()持久化键值对
这些操作的底层都依赖于 ContextImpl 中持有的 LoadedApk mPackageInfo 对象:
// frameworks/base/core/java/android/app/ContextImpl.java |
5.1 Multi-APK 场景下的 Context
Android 5.0+ 支持 split APK(包括 Dynamic Delivery)。ContextImpl 通过 LoadedApk 的 splitResDirs 处理多 APK 资源:
// LoadedApk 持有 |
这使得不同模块(如 instant app 的 feature module)的资源可以被 Context 正确访问,而无需在同一 APK 中。
六、ContextCompat 适配机制
AndroidX 的 ContextCompat 封装了不同 API 版本的差异:
// ContextCompat 统一旧版本缺失的 API |
七、核心面试题
Q1:Application Context 和 Activity Context 有什么区别?什么时候用哪个?
核心区别在于生命周期和窗口关联能力。Application Context 生命周期等于进程生命周期,适合用于单例、数据库、网络库初始化等需要全局 Context 的场景。Activity Context 带有主题和窗口令牌,适合用于 UI 相关操作(显示 Dialog、inflate layout 需要 Activity 主题、startActivity 不带 NEW_TASK 等)。一个关键原则:只要生命周期需要跟随 Activity 的资源(如 Dialog、Toast 的某些场景),就用 Activity Context;否则优先使用 Application Context 避免内存泄漏。
Q2:getApplication() 和 getApplicationContext() 返回的是同一个对象吗?
是同一个对象。getApplication() 返回的是当前进程的 Application 实例(继承自 ContextWrapper),getApplicationContext() 返回的是 Context 实例。在 Activity 或 Service 中,getApplicationContext() 返回的就是 Application 对象本身(因为 Application 实现了 Context 接口链)。从语义上说,getApplicationContext() 强调的是 “这是一个适合长时间持有的 Context”,而 getApplication() 返回的是其 Application 类型。
Q3:为什么 Activity 可以直接调用 startActivity 而不需要 FLAG_ACTIVITY_NEW_TASK?
因为 Activity 继承了 ContextThemeWrapper→ContextWrapper→Context,其内部的 ContextImpl 持有 Activity 的 token(与 WMS 的 IApplicationToken 关联),在调用 startActivity 时,系统会根据调用者的 token 自动确定目标 Activity 放入哪个 Task。而 Application Context 不持有任何 Activity 的 token,因此必须在 Intent 中显式设置 FLAG_ACTIVITY_NEW_TASK。
Q4:ContextImpl 中的 mOpPackageName 和 mBasePackageName 有什么区别?
mBasePackageName 是当前 Context 对应的包名(如 “com.example.app”)。mOpPackageName 是”操作包名”——当 App A 通过 createPackageContext("com.example.b", ...) 创建 App B 的 Context 时,mBasePackageName 是 “com.example.b”,但 mOpPackageName 是 “com.example.a”。系统在权限检查(如 IPC 调用者身份验证)时使用 mOpPackageName,确保调用者的身份不被伪造。
Q5:为什么 BroadcastReceiver.onReceive 中的 Context 限制不能 registerReceiver 和 bindService?
BroadcastReceiver 的 onReceive 执行时间通常非常短(前台广播 10 秒,后台广播 60 秒),之后系统认为 Receiver 已经完成工作并可能杀死进程。如果在 onReceive 中注册了新的 BroadcastReceiver 或绑定了 Service,这些注册/绑定会被”遗忘”(因为 Receiver 生命周期结束后无法正确清理),导致系统资源泄漏。因此 Android 通过 ReceiverRestrictedContext 禁止这些操作。
5.2 Context.getExternalFilesDir 的实现与环境判断
getExternalFilesDir() 和 getExternalCacheDir() 的实现最终委托到 mPackageInfo(LoadedApk):
// ContextImpl.java |
对于多用户支持,实际路径为 /storage/emulated/<userId>/Android/data/<pkg>/files/。
5.3 MutableContextWrapper 的使用场景
MutableContextWrapper 是一个可以动态替换 base Context 的特殊 ContextWrapper:
public class MutableContextWrapper extends ContextWrapper { |
典型使用场景:在 LayoutInflater 中——当从 XML 中 inflate View 时,View 需要持有 XML 声明时的 Context(而非创建时的 Context)。MutableContextWrapper 允许在 inflate 过程中切换 base Context。
5.4 Context 与进程生命周期
Context 在进程生命周期中扮演关键角色:
- 当应用进程启动时,ActivityThread 创建 Application Context
- 当所有 Activity 销毁且没有活跃的 Service/BroadcastReceiver 时,进程进入”空进程”状态
- LMK 可以随时杀死空进程,此时 Application Context 对应的 Application 对象被 GC
Context 的引用链对 GC 的影响:静态变量持有 Activity Context → Activity 无法 GC → 即使 finish 的 Activity 也无法释放 → 持续占用内存。这是最常见的 Android 内存泄漏模式。
六、跨进程 Context 场景
6.1 createPackageContext 的使用与限制
// 创建其他应用的 Context(需要共享 UID 或适当权限) |
此方法在 Android 11+ 中有严格限制——新增了 CONTEXT_CREDENTIAL_PROTECTED_STORAGE flag 用于区分 FBE 加密存储的 CE(凭据加密)和 DE(设备加密)目录。
6.2 Context 的 display 关联
Android 8.0+ 支持多显示设备(如外接显示器、折叠屏)。Activity Context 的 displayId 决定了它渲染在哪个屏幕上:
// ContextImpl 中存储 displayId |
AOSP 核心路径参考:
frameworks/base/core/java/android/content/Context.javaframeworks/base/core/java/android/content/ContextWrapper.javaframeworks/base/core/java/android/app/ContextImpl.javaframeworks/base/core/java/android/app/SystemServiceRegistry.javaframeworks/base/core/java/android/view/ContextThemeWrapper.javaframeworks/base/core/java/android/app/ActivityThread.javaframeworks/base/core/java/android/app/LoadedApk.java
七、Context 的设计模式分析
7.1 装饰器模式(Decorator Pattern)
Context 的继承体系是装饰器模式的典型案例。ContextWrapper 是 Decorator,ContextImpl 是 ConcreteComponent,Activity/Service/Application 是 ConcreteDecorator。每一层 Decorator 可以增强(override)某些方法,而将其他方法委托给下一层。
这种设计的优势:
- 单一职责:ContextImpl 负责所有实际的系统交互,Wrapper 子类各自增强特定功能
- 开闭原则:新增 Context 子类(如 MutableContextWrapper)不需要修改 ContextImpl
- 组合优于继承:ContextWrapper 持有 ContextImpl 引用而非继承它,避免了代码重复
7.2 Context 的工厂方法模式
ContextImpl.createAppContext()、createActivityContext() 等静态方法构成了工厂方法模式——根据不同的使用场景创建不同配置的 ContextImpl 实例。这使得 ContextImpl 的构造函数保持简单,复杂的配置逻辑集中在工厂方法中。
7.3 为什么 Context 设计为抽象类而非接口
早期 Android 设计选择将 Context 设计为 abstract class 而非 interface,主要原因是需要为系统服务提供默认实现(在 API 1 中)。如果 Context 是 interface,每次添加新方法都会破坏所有实现类。abstract class 可以通过提供默认实现(哪怕是抛出 UnsupportedOperationException)来保持向后兼容。




