目录
  1. 1. 一、Context 的继承体系
    1. 1.1. 1.1 类图
    2. 1.2. 1.2 ContextWrapper——装饰器模式的经典应用
  2. 2. 二、ContextImpl——Context 的真正实现
    1. 2.1. 2.1 ContextImpl 的核心字段
    2. 2.2. 2.2 ContextImpl 的创建
    3. 2.3. 2.3 ActivityThread 中创建 Context
  3. 3. 三、不同类型 Context 的差异
    1. 3.1. 3.1 Application Context vs Activity Context
    2. 3.2. 3.2 getSystemService 的实现
    3. 3.3. 3.3 Service 的 Context
      1. 3.3.1. 3.4 BroadcastReceiver 的 Context
    4. 3.4. 3.5 ContentProvider 的 Context
  4. 4. 四、Context 的内存泄漏分析
    1. 4.1. 4.1 常见泄漏场景
  5. 5. 五、Context 在资源加载中的角色
    1. 5.0.1. 5.1 Multi-APK 场景下的 Context
  • 6. 六、ContextCompat 适配机制
  • 7. 七、核心面试题
    1. 7.1. 5.2 Context.getExternalFilesDir 的实现与环境判断
    2. 7.2. 5.3 MutableContextWrapper 的使用场景
    3. 7.3. 5.4 Context 与进程生命周期
    4. 7.4. 六、跨进程 Context 场景
      1. 7.4.1. 6.1 createPackageContext 的使用与限制
      2. 7.4.2. 6.2 Context 的 display 关联
  • 8. 七、Context 的设计模式分析
    1. 8.1. 7.1 装饰器模式(Decorator Pattern)
    2. 8.2. 7.2 Context 的工厂方法模式
    3. 8.3. 7.3 为什么 Context 设计为抽象类而非接口
  • 【吃透源码系列】之Context

    Context 在 Android 中被描述为 “关于应用环境的全局信息接口”。它贯穿整个 Android 框架,是访问系统资源、启动组件、获取系统服务的入口。然而,Context 的继承层级、不同类型的 Context 之间的差异、以及使用不当可能导致的内存泄漏,是深入理解 Android 框架不可或缺的知识。本文深入分析 Context 的继承体系、ContextImpl 的创建过程以及各类型 Context 的差异。

    一、Context 的继承体系

    1.1 类图

    abstract Context

    abstract ContextWrapper

    ├── ContextThemeWrapper → Activity
    ├── Service → Service
    ├── Application → Application
    └── ContextImpl (真正的实现——所有 Context 背后都是它)

    从 ContextImpl 角度更准确地表达:

    Context (abstract)
    ├── ContextImpl (真正的实现——每种 Context 场景背后都是它)
    └── ContextWrapper (装饰器基类)
    ├── ContextThemeWrapper (带主题的 Context)
    │ └── Activity (每个 Activity 对应一个 ContextThemeWrapper)
    ├── Service (Service 的 Context)
    ├── Application (Application 的 Context)
    └── MutableContextWrapper (可动态替换 base 的 ContextWrapper)

    AOSP 路径:

    • frameworks/base/core/java/android/content/Context.java
    • frameworks/base/core/java/android/content/ContextWrapper.java
    • frameworks/base/core/java/android/app/ContextImpl.java
    • frameworks/base/core/java/android/view/ContextThemeWrapper.java

    1.2 ContextWrapper——装饰器模式的经典应用

    ContextWrapper 是装饰器模式的典型实现,它持有一个 Context 引用(mBase),所有方法默认委托给 mBase:

    // frameworks/base/core/java/android/content/ContextWrapper.java
    public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
    mBase = base;
    }

    // 将 mBase 指向真正的实现
    protected void attachBaseContext(Context base) {
    if (mBase != null) {
    throw new IllegalStateException("Base context already set");
    }
    mBase = base;
    }

    @Override
    public ContentResolver getContentResolver() {
    return mBase.getContentResolver(); // 委托给 mBase
    }

    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
    return mBase.getSharedPreferences(name, mode);
    }

    @Override
    public Object getSystemService(@ServiceName @NonNull String name) {
    return mBase.getSystemService(name);
    }

    @Override
    public void startActivity(Intent intent) {
    mBase.startActivity(intent);
    }
    }

    这一设计的精妙之处在于: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
    class ContextImpl extends Context {
    final @NonNull ActivityThread mMainThread; // 主线程 ActivityThread 引用
    final @NonNull LoadedApk mPackageInfo; // APK 加载信息
    private @Nullable ActivityThread mUiThread; // UI 线程
    private @NonNull String mBasePackageName; // 基础包名
    private @NonNull String mOpPackageName; // 操作包名(跨包调用时区分)
    private final @NonNull ResourcesManager mResourcesManager;
    private @NonNull Resources mResources; // 资源访问
    private @Nullable Display mDisplay; // 显示设备
    private final int mFlags; // Context 创建标志
    private @NonNull final UserHandle mUser; // 多用户
    private @Nullable IBinder mActivityToken; // Activity 的 Binder token
    }

    mActivityToken 是区分 Application Context 和 Activity Context 的关键字段。Application Context 的 mActivityToken 为 null,而 Activity Context 持有 ActivityRecord 的 token。这个 token 在 startActivitygetSystemService(WINDOW_SERVICE) 等需要窗口令牌的操作中起关键作用。

    mOpPackageNamemBasePackageName 的区别:当 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
    // 为 Application 创建 ContextImpl
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
    return createAppContext(mainThread, packageInfo, null);
    }

    // 为 Activity 创建 ContextImpl
    static ContextImpl createActivityContext(ActivityThread mainThread,
    LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken,
    int displayId, Configuration overrideConfiguration) {
    // Activity 的 ContextImpl 带有 activityToken(与 WMS 窗口关联)和 displayId
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ...);
    context.setResources(resourcesManager.createBaseActivityResources(
    activityToken, ...));
    return context;
    }

    2.3 ActivityThread 中创建 Context

    // frameworks/base/core/java/android/app/ActivityThread.java
    // 创建 Activity 的 Context
    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
    ContextImpl appContext = ContextImpl.createActivityContext(
    this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
    // ...
    return appContext;
    }

    // 创建 Application
    public Application makeApplication(boolean forceDefaultAppClass,
    Instrumentation instrumentation) {
    if (mApplication != null) return mApplication;

    Application app = (Application) clazz.newInstance();
    // 创建 Application 用的 ContextImpl
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    // 绑定
    app.attach(appContext);
    mApplication = app;
    return app;
    }

    三、不同类型 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
    @Override
    public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
    }

    // frameworks/base/core/java/android/app/SystemServiceRegistry.java
    final class SystemServiceRegistry {
    // 静态代码块注册所有服务工厂
    static {
    registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
    new CachedServiceFetcher<ActivityManager>() {
    @Override
    public ActivityManager createService(ContextImpl ctx) {
    return new ActivityManager(
    ctx.getOuterContext(), // outer: Activity or Application
    ctx.mMainThread.getHandler());
    }
    });

    registerService(Context.WINDOW_SERVICE, WindowManager.class,
    new CachedServiceFetcher<WindowManager>() {
    @Override
    public WindowManager createService(ContextImpl ctx) {
    return new WindowManagerImpl(ctx.mDisplay);
    }
    });

    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
    new CachedServiceFetcher<LayoutInflater>() {
    @Override
    public LayoutInflater createService(ContextImpl ctx) {
    return new PhoneLayoutInflater(ctx.getOuterContext());
    }
    });
    // ... 更多服务注册
    }
    }

    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
    public abstract class Service extends ContextWrapper
    implements ComponentCallbacks2 {

    public Service() {
    super(null);
    }

    @Override
    public final void attach(Context context, ...) {
    attachBaseContext(context); // 将 ContextImpl 设为 mBase
    }
    }

    Service 直接继承 ContextWrapper(不是 ContextThemeWrapper),因此它没有主题支持,这也就是为什么你不能在 Service 中 inflate layout 并使用 Activity 主题。

    3.4 BroadcastReceiver 的 Context

    // BroadcastReceiver.onReceive(Context context, Intent intent)
    public abstract void onReceive(Context context, Intent intent);

    onReceive 中传入的 Context 是 ReceiverRestrictedContext(ContextWrapper 的子类)。这个 Context 不能调用 registerReceiverbindService——因为 Receiver 的生命周期短(onReceive 执行完后的几秒内就可能被系统销毁),注册的 receiver 和绑定的 service 会泄漏。

    3.5 ContentProvider 的 Context

    ContentProvider 在 onCreate() 被调用前通过 attachInfo() 方法绑定 Context。它同样继承 ContextWrapper,生命周期与 Application 相同:

    // frameworks/base/core/java/android/content/ContentProvider.java
    public void attachInfo(Context context, ProviderInfo info) {
    attachBaseContext(context); // 绑定的通常是 Application Context
    mContext = context;
    // ...
    }

    四、Context 的内存泄漏分析

    4.1 常见泄漏场景

    场景1:静态变量持有 Activity Context

    public class MyManager {
    private static MyManager sInstance;
    private Context mContext;

    private MyManager(Context context) {
    mContext = context; // 如果传入的是 Activity,泄漏了!
    }

    public static MyManager getInstance(Context context) {
    if (sInstance == null) {
    sInstance = new MyManager(context);
    }
    return sInstance;
    }
    }

    解法:使用 context.getApplicationContext() 或让静态变量持有 Application Context。

    场景2:单例持有 Activity 引用:如 EventBus 注册了 Activity 但未解注册。

    场景3:内部类/匿名类隐式持有外部类引用:如 Handler、AsyncTask 作为 Activity 的非静态内部类,它们持有对外部 Activity 的隐式引用。如果 Handler 消息延迟较长,Activity 在消息执行前被销毁,就会泄漏。

    场景4:Toast 泄漏

    // 错误:使用 Activity Context 创建 Toast
    Toast.makeText(activity, "Message", Toast.LENGTH_LONG).show();

    如果 Activity 销毁时 Toast 尚未消失,Toast 的内部 TN binder 持有 Activity 的 Context 引用,导致泄漏。推荐使用 getApplicationContext()

    五、Context 在资源加载中的角色

    ContextImpl 持有 Resources 对象,负责:

    1. 资源加载getResources().getString(R.string.xxx)getResources().getDrawable(R.drawable.xxx)
    2. AssetManager:管理 APK 内的 assets 目录文件
    3. 数据库openOrCreateDatabase() 创建 SQLiteDatabase
    4. 文件操作openFileInput()openFileOutput()
    5. SharedPreferencesgetSharedPreferences() 持久化键值对

    这些操作的底层都依赖于 ContextImpl 中持有的 LoadedApk mPackageInfo 对象:

    // frameworks/base/core/java/android/app/ContextImpl.java
    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
    return mPackageInfo.getSharedPreferences(name, mode);
    }

    @Override
    public File getDataDir() {
    return mPackageInfo.getDataDir();
    }

    5.1 Multi-APK 场景下的 Context

    Android 5.0+ 支持 split APK(包括 Dynamic Delivery)。ContextImpl 通过 LoadedApksplitResDirs 处理多 APK 资源:

    // LoadedApk 持有
    private final String[] mSplitResDirs; // split APK 的 res 目录

    // 创建 Resources 时
    Resources resources = ResourcesManager.getInstance().getResources(
    null, mResDir, splitResDirs, mOverlayDirs, mOverlayPaths, ...);

    这使得不同模块(如 instant app 的 feature module)的资源可以被 Context 正确访问,而无需在同一 APK 中。

    六、ContextCompat 适配机制

    AndroidX 的 ContextCompat 封装了不同 API 版本的差异:

    // ContextCompat 统一旧版本缺失的 API
    public static int getColor(Context context, int id) {
    if (Build.VERSION.SDK_INT >= 23) {
    return context.getColor(id);
    } else {
    return context.getResources().getColor(id);
    }
    }

    public static void startActivity(Context context, Intent intent, Bundle options) {
    if (Build.VERSION.SDK_INT >= 16) {
    context.startActivity(intent, options);
    } else {
    context.startActivity(intent);
    }
    }

    七、核心面试题

    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
    @Override
    public File getExternalFilesDir(String type) {
    File[] dirs = mPackageInfo.getExternalFilesDirs(type);
    return dirs != null && dirs.length > 0 ? dirs[0] : null;
    }

    对于多用户支持,实际路径为 /storage/emulated/<userId>/Android/data/<pkg>/files/

    5.3 MutableContextWrapper 的使用场景

    MutableContextWrapper 是一个可以动态替换 base Context 的特殊 ContextWrapper:

    public class MutableContextWrapper extends ContextWrapper {
    public MutableContextWrapper(Context base) {
    super(base);
    }

    public void setBaseContext(Context base) {
    mBase = base; // 无视 "Base context already set" 限制
    }
    }

    典型使用场景:在 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 或适当权限)
    Context otherAppContext = createPackageContext("com.other.app",
    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);

    此方法在 Android 11+ 中有严格限制——新增了 CONTEXT_CREDENTIAL_PROTECTED_STORAGE flag 用于区分 FBE 加密存储的 CE(凭据加密)和 DE(设备加密)目录。

    6.2 Context 的 display 关联

    Android 8.0+ 支持多显示设备(如外接显示器、折叠屏)。Activity Context 的 displayId 决定了它渲染在哪个屏幕上:

    // ContextImpl 中存储 displayId
    private @Nullable Display mDisplay;

    // Activity Context 的 displayId 在 createActivityContext 时确定
    // Application Context 的 displayId 为 DEFAULT_DISPLAY

    AOSP 核心路径参考:

    • frameworks/base/core/java/android/content/Context.java
    • frameworks/base/core/java/android/content/ContextWrapper.java
    • frameworks/base/core/java/android/app/ContextImpl.java
    • frameworks/base/core/java/android/app/SystemServiceRegistry.java
    • frameworks/base/core/java/android/view/ContextThemeWrapper.java
    • frameworks/base/core/java/android/app/ActivityThread.java
    • frameworks/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)来保持向后兼容。

    打赏
    • 微信
    • 支付宝

    评论