关于Context和ContextImpl还有ContextWrapper的关系

文章详细阐述了Context、ContextImpl和ContextWrapper在Android系统中的关系。Context是一个抽象类,由ContextImpl实现;ContextWrapper则作为Context的包装类,用于扩展Context的功能。ContextImpl主要在创建Application时通过ContextImpl.createAppContext生成,然后通过ContextWrapper的attachBaseContext方法将ContextImpl与ContextWrapper关联起来,确保对Context的操作最终调用到ContextImpl的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于Context和ContextImpl还有ContextWrapper的关系

1.Context和ContextImpl还有ContextWrapper的关系

img

​ 图一.Context和ContextImpl还有ContextWrapper的关系示意图

1.1.ContextImpl是Context的实现类

从Context和ContextImpl的源代码中,可以看出Context是一个抽象类,具体的实现类是ContextImpl

public abstract class Context {

Context.java文件

class ContextImpl extends Context {....}

ContextImpl.java文件

1.2.ContextWrapper是Context的包装类

从ContextWrapper的源代码可以看出,ContextWrapper继承自Context; 并且ContextWrapper类持有Context的引用;

所以最终是要通过java的多态这个属性,调用Context类的相关方法,实际上是调用ContextImpl类里面的方法

public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

ContextWrapper.java文件

2.ContextImpl跟ContextWrapper的关联过程

2.1.ContextWrapper可以关联的可能入口

根据下面的ContextWrapper的源码的解读,如果ContextWrapper关联ContextImpl,

只有两个地方可以把Context作为参数传进来

第一个:ContextWrapper的构造函数

第二个:attachBaseContext函数

这两个函数都会把context 赋值给名字为mBase的Context的属性

public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

    @GuardedBy("mLock")
    @VisibleForTesting
    public List<ComponentCallbacks> mCallbacksRegisteredToSuper;

    private final Object mLock = new Object();

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

    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

2.2.Context创建过程

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        
             //TODO 
             ......
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            // The network security config needs to be aware of multiple
            // applications in the same process to handle discrepancies
            NetworkSecurityConfigProvider.handleNewApplication(appContext);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + " package " + mPackageName + ": " + e.toString(), e);
            }
        }
        //TODO 
        ......

        return app;
    }

LoadedApk.java

在LoadedApk的函数makeApplication中,下面这段语句通过调用了ContextImpl.createAppContext 生成了ContextImpl对象

ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

在ContextImpl类中,createAppContext函数,调用了另外一个带3个参数的重写函数createAppContext,

可以看出在这个函数里面,通过ContextImpl context = new ContextImpl 这个语句 创建了ContextImpl类的对象,

然后通过函数返回,返回了这个ContextImpl类的对象.

@UnsupportedAppUsage
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return createAppContext(mainThread, packageInfo, null);
    }

    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
            String opPackageName) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
            ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
        context.setResources(packageInfo.getResources());
        context.mContextType = isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
                : CONTEXT_TYPE_NON_UI;
        return context;
    }

上面的步骤创建了appContext,然后通过下面的函数调用把对象appContext传递下去

app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);

在Instrumentation源代码中,newApplication 函数中,Application的attach函数,通过此函数, 把context传递下去

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        app.attach(context);
        return app;
    }

Instrumentation.java

Application类的attach函数,在这个函数中,会把传递进来的context 传递给attachBaseContext函数, attachBaseContext 函数是父类ContextWrapper的函数

同时验证了上面的结论(2.1.ContextWrapper可以关联的可能入口)

public class Application extends ContextWrapper implements ComponentCallbacks2 {
    @UnsupportedAppUsage
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

Application.java

在ContextWrapper的源代码中,可以看到context会在attachBaseContext中赋值给名字为mBase的Context对象的属性

public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

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

    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     *
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

ContextWrapper.java

到此处为止,ContextImpl 跟ContextWrapper 的关联已经完成

引用:
Android 开发者,你真的懂Context吗?

<think>我们正在讨论Activity ContextApplication Context的区别以及隔离机制的原因。根据用户的问题之前的引用,我们需要详细解释这两种Context的区别,以及为什么需要隔离机制。 首先,回顾引用内容: - 引用[1]提到:绝大多数情况下,使用在组件内部直接获取的Context。只要引用不超过组件的生命周期,可以安全保存。如果保存的Context引用超过了Activity或Service的生命周期,就需要转换为Application Context。 - 引用[2]补充:在Fragment或View中获取Application Context,需要先得到Activity的Context,再通过它得到Application Context。 现在,针对用户的问题: 1. **Activity Context与Application Context的区别**: - **Activity Context**:与Activity实例关联,包含了Activity的特定信息,如主题、窗口等。它的生命周期与Activity相同,当Activity被销毁时,该Context也会被销毁。 - **Application Context**:与整个应用关联,生命周期从应用启动到应用终止。它不包含界面相关的信息(如主题),因此不能用于直接启动Activity或显示对话框等需要界面元素的操作。 2. **为什么需要隔离机制**: - **生命周期管理**:隔离机制确保每个组件的资源在其生命周期内有效。如果使用Activity Context的地方错误地使用了Application Context,可能会导致内存泄漏(因为Application Context生命周期长,持有Activity的引用导致Activity无法被回收)或者资源泄露。 - **功能隔离**:Activity Context具有界面相关的功能(如启动Activity、显示对话框),而Application Context更适合于获取全局资源、启动服务、发送广播等。隔离机制防止了不恰当的使用,比如用Application Context显示对话框会导致崩溃(因为缺少Activity的窗口令牌)。 - **安全性**:通过隔离,系统可以检查操作是否在正确的上下文中执行。例如,启动Activity需要任务栈信息,而Application Context没有任务栈,因此必须使用`FLAG_ACTIVITY_NEW_TASK`标志。 接下来,我们按照用户的要求组织答案: ### 一、核心区别 1. **生命周期范围** - `Activity Context`:绑定到Activity生命周期,随Activity销毁而释放[^1]。 - `Application Context`:贯穿整个应用进程生命周期,从`onCreate()`到进程终止[^1]。 2. **功能差异** | 功能 | Activity Context | Application Context | |-------------------------|------------------|---------------------| | 启动Activity | ✅ (自动关联任务栈) | ❌ (需`FLAG_ACTIVITY_NEW_TASK`)| | 显示Dialog | ✅ | ❌ (导致`WindowManager$BadTokenException`)| | 布局膨胀(`inflate()`) | ✅ (携带当前主题) | ✅ (无主题样式)| | 发送广播 | ✅ | ✅ (更安全)| | 获取系统服务 | ✅ | ✅ | ### 二、隔离机制的必要性 1. **避免内存泄漏** - 若长期持有`Activity Context`(如静态变量),会导致Activity无法被GC回收[^1]。 - 正确做法:跨生命周期操作使用`Application Context`。 2. **防止资源错乱** - `Activity Context`包含当前主题资源,而`Application Context`使用默认主题: ```java // Activity中获取主题 getTheme().resolveAttribute(R.attr.colorPrimary, color, true); // Application中无界面主题 ``` - 使用错误Context加载布局可能导致样式异常。 3. **窗口令牌安全** - 系统级窗口操作(如弹窗)需验证`WindowToken`: - `Activity Context`隐含有效的`WindowToken`; - `Application Context`无此令牌,强行使用会崩溃。 ### 三、典型场景示例 1. **应使用Activity Context的场景** ```java // 显示Dialog(必须Activity Context) AlertDialog dialog = new AlertDialog.Builder(MainActivity.this).create(); ``` ```java // 启动同任务栈的Activity(无需特殊标志) startActivity(new Intent(MainActivity.this, NextActivity.class)); ``` 2. **应使用Application Context的场景** ```java // 获取全局数据库单例(生命周期与进程一致) MyDatabase db = MyDatabase.getInstance(getApplicationContext()); ``` ```java // 发送广播(避免持有Activity引用) sendBroadcast(new Intent("ACTION"), getApplicationContext()); ``` ### 四、设计原理图 ```mermaid graph LR A[Context抽象接口] --> B[ContextImpl功能实现] C[Activity Context] -- 委托 --> B D[Application Context] -- 委托 --> B E[Service Context] -- 委托 --> B C --> F[附加:窗口令牌/主题] D --> G[附加:全局资源池] ``` > **总结**:Android通过`Context`代理模型(由`ContextImpl`实际承载功能)实现统一接口,再通过子类(如`Activity`、`Application`)扩展**生命周期管理****资源隔离**能力。隔离机制的核心目的是: > 1. 确保界面操作安全(窗口令牌验证); > 2. 优化内存管理(避免长生命周期引用短生命周期对象); > 3. 维护正确的资源作用域(主题/样式隔离)[^1][^2]。 --- §§相关问题§§ 1. 为什么静态持有Activity Context会导致内存泄漏?如何检测这类问题? 2. 在BroadcastReceiver中应该使用哪种Context,为什么? 3. ContextImpl是如何实现不同Context类型的功能分发的?</think>### Activity Context与Application Context的区别及隔离机制原因分析 #### 一、核心区别 1. **生命周期范围** - `Activity Context`:绑定到Activity生命周期,随Activity销毁而释放[^1] - `Application Context`:贯穿整个应用进程生命周期,从`onCreate()`到进程终止[^1] 2. **功能差异** | 功能 | Activity Context | Application Context | |-------------------------|------------------|---------------------| | 启动Activity | ✅ (自动关联任务栈) | ❌ (需`FLAG_ACTIVITY_NEW_TASK`)| | 显示Dialog | ✅ | ❌ (导致`WindowManager$BadTokenException`)| | 布局膨胀(`inflate()`) | ✅ (携带当前主题) | ✅ (无主题样式)| | 发送广播 | ✅ | ✅ (更安全)| | 获取系统服务 | ✅ | ✅ | 3. **资源访问特性** ```java // Activity Context携带当前界面主题 getTheme().resolveAttribute(R.attr.colorPrimary, color, true); // Application Context使用默认主题 getApplicationContext().getTheme(); // 返回基础主题 ``` #### 二、隔离机制的必要性 1. **避免内存泄漏** - 若长期持有`Activity Context`(如静态变量),会导致Activity无法被GC回收[^1] - 正确做法:跨生命周期操作使用`Application Context` ```java // 错误:静态持有Activity引用 static Context sContext = activityContext; // 正确:使用Application Context static Context sContext = activity.getApplicationContext(); ``` 2. **防止资源错乱** - `Activity Context`包含当前主题资源,而`Application Context`使用默认主题 - 示例:若用Application Context加载布局 ```xml <!-- 布局中使用?attr/colorPrimary --> <View android:background="?attr/colorPrimary"/> ``` 结果:无法获取Activity特定主题值,显示默认颜色 3. **窗口令牌安全** - 系统级窗口操作需验证`WindowToken`: - Activity隐含有效的`WindowToken` - Application无此令牌,强行使用弹窗会导致崩溃: ```log android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid ``` #### 三、设计原理图解 ```mermaid graph TD A[Context抽象接口] --> B[ContextImpl功能实现] C[Activity Context] -- 委托 --> B D[Application Context] -- 委托 --> B E[Service Context] -- 委托 --> B C --> F[附加:窗口令牌/主题] D --> G[附加:全局资源池] ``` #### 四、使用场景准则 1. **优先使用Activity Context** ```java // 界面相关操作 new AlertDialog.Builder(this); // this指Activity startActivity(new Intent(this, NextActivity.class)); ``` 2. **必须使用Application Context** ```java // 跨生命周期的单例 DatabaseHelper.init(getApplicationContext()); // 长时间运行的Service startService(new Intent(getApplicationContext(), MyService.class)); ``` 3. **Fragment/View中获取Context** ```java // 在Fragment中(引用[2]) Context appContext = getActivity().getApplicationContext(); // 在自定义View中 Context appContext = getContext().getApplicationContext(); ``` > **隔离机制本质**:Android通过`ContextWrapper`代理模式实现统一接口,再通过子类扩展**生命周期管理****资源隔离**能力。这种设计既保证功能一致性(通过`ContextImpl`),又满足组件安全隔离需求,防止界面操作越权、资源泄露内存泄漏[^1][^2]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦想全栈程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值