Android | Context 全解析:原理、类型与使用指南

Context 是 Android 系统提供的 全局环境信息接口,用于管理 应用资源、组件交互、系统服务访问 等。主要作用包括:

  • 访问资源(getResources()、getAssets())
  • 启动组件(startActivity()、startService()、sendBroadcast())
  • 管理应用信息(getPackageManager()、getPackageName())
  • 访问系统服务(getSystemService())
  • 操作 SharedPreferences(getSharedPreferences())
  • 创建视图(LayoutInflater.from(context))

Context 知识图谱

kotlin 复制代码
Context(抽象类)
│
├── ContextWrapper(mBase)
│   ├── ContextThemeWrapper(带主题的封装类)
│   │   ├── Activity
│   │
│   ├── Service
│   │
│   ├── Application
│   │
│   ├──MutableContextWrapper(动态改变context)
│   │
├── ContextImpl (Context的实现类,系统底层实现)

Activity、Service、Application都是继承自ContextWrapper,而ContextWrapper又继承自Context,所以一个App中 Context的数量 = Activity数量 + Service数量 + 1(Application)

kotlin 复制代码
//ContextWrapper.java
public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

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

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

Context采用装饰者模式,ContextWrapper是Context子类,内部都会调用传入的mBase相关方法(mBase其实就是ContextImpl),而Activity、Service、Application都是ContextWrapper的子类,所以它们都是跟ContextImpl关联的

kotlin 复制代码
           ┌──────────────┐
           │  Context (抽象类) │
           └──────┬──────┘
                  │
   ┌─────────────┴────────────┐
   │                          │
   ▼                          ▼
┌──────────────┐      ┌─────────────────┐
│ ContextImpl  │      │  ContextWrapper │
│ (真正实现)    │      │ (持有 mBase 代理) │
└──────────────┘      └────────┬────────┘
                               │
                               ▼
                 ┌──────────────────────────┐
                  Activity / Service / Application 等   │
                 └──────────────────────────┘

Context/ContextWrapper/ContextImpl 三者关系:ContextWrapper、ContextImpl继承自Context,其中ContextImpl 是 Context 的具体实现,ContextWrapper 作为包装类持有 Context(通常是 ContextImpl),对其进行代理和扩展

下面一起来看下Application、Activity、Service都是如何初始化并绑定Context的吧。

Application

kotlin 复制代码
//ActivityThread.java
//系统进程(SystemServer)创建ActivityThread并初始化
public static ActivityThread systemMain() {
    ThreadedRenderer.initForSystemProcess();
    ActivityThread thread = new ActivityThread();
    thread.attach(true, 0);
    return thread;
}

//应用进程创建ActivityThread并初始化
public static void main(String[] args) {
   //...其他代码...
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    Looper.loop();
}

main() 用于应用进程启动ActivityThread,并进入 Looper 消息循环,确保主线程可以处理消息,并且执行了thread.attach方法。

kotlin 复制代码
//ActivityThread.java
private void attach(boolean system, long startSeq) {
  //应用测试和生命周期管理的核心组件,负责拦截 Activity 生命周期回调(如 onCreate())
  mInstrumentation = new Instrumentation();
  mInstrumentation.basicInit(this);
  //1、创建应用的 Context
  ContextImpl context = ContextImpl.createAppContext(this, getSystemContext().mPackageInfo);
  //2、实例化 Application 对象
  mInitialApplication = context.mPackageInfo.makeApplication(true, null);
  //调用application的onCreate()进行初始化
  mInitialApplication.onCreate();
}

上面代码中有两个个主要操作,1处是创建应用的Context,也就是创建ContextImpl:

kotlin 复制代码
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
        String opPackageName) {
    //...忽略其他代码...
    //创建应用的Context
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
        ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
    context.setResources(packageInfo.getResources());
    //...忽略其他代码...
    return context;
}

2处是通过makeApplication()通过反射初始化了Application:

kotlin 复制代码
//Instrumentation.java
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;
}

//AppComponentFactory.java
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
        @NonNull String className)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    //通过反射初始化Application
    return (Application) cl.loadClass(className).newInstance();
}

//Application.java
final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

可以看到最终是在Application#attach()方法中调用父类ContextWrapper#attachBaseContext()来设置context。

Activity

Activity 继承 ContextThemeWrapper(拥有界面主题和窗口管理能力)。可以 启动 UI 相关组件(如 Dialog、startActivity())。

AMS (ActivityManagerService) 请求应用进程创建Activity时,ActivityThread#performLaunchActivity() 是启动Activity时的核心方法,主要完成了 Activity 的创建、绑定 Application、执行 attach() 等逻辑。

kotlin 复制代码
//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ActivityInfo aInfo = r.activityInfo;
    //ContextImpl是Activity运行所需的真正Context,并用于后续的attach()绑定。
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //通过反射机制创建Activity对象。
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    } catch (Exception e) {
    }

    try {
        //Application只会在应用首次启动时创建一次,并在所有Activity之间共享
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (activity != null) {
            appContext.setOuterContext(activity);
            //绑定各种信息,具体见下面说明
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback,
                    r.assistToken, r.shareableActivityToken);
        }
    } catch (SuperNotCalledException e) {
        throw e;
   }
    return activity;
}

上面代码中的注释很详细,可以看到Activity初始化后,通过attach()方法绑定各种信息,那就继续看看这个方法都做了什么。

kotlin 复制代码
//Activity.java
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
        IBinder shareableActivityToken) {
    //这里调用的是父类ContextWrapper中的方法,将context赋值给父类的mBase
    attachBaseContext(context);
    
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mUiThread = Thread.currentThread();
    //获取application
    mApplication = application;
    mIntent = intent;
    //...省略部分代码...
}

attach()绑定了各种信息,主要完成如下:

  • 绑定 Context(将 Activity 绑定到 ContextImpl)
  • 绑定 AMS(ActivityManager)
  • 绑定 Application 实例
  • 绑定 Window 和 PhoneWindow(管理 UI 视图)
  • 初始化 Activity 相关参数

这一步完成后,Activity 就拥有了 Context,能够调用 getApplicationContext()getResources() 等方法。

Service

当 AMS (ActivityManagerService) 请求应用进程创建 Service 时,会调用ActivityThread#handleCreateService()在应用进程中创建 Service 并完成初始化。

kotlin 复制代码
//handleCreateService()负责创建并启动Service
private void handleCreateService(CreateServiceData data) {
   Service service = null;
   //LoadedApk 代表 APK 在内存中的加载信息,用于创建 Application 和 Service
   LoadedApk packageInfo = getPackageInfoNoCheck(
        data.info.applicationInfo, data.compatInfo);
   Application app = packageInfo.makeApplication(false, mInstrumentation);
   final java.lang.ClassLoader cl;
   //获取类加载器ClassLoader
   if (data.info.splitName != null) {
       cl = packageInfo.getSplitClassLoader(data.info.splitName);
   } else {
       cl = packageInfo.getClassLoader();
   }
   //反射创建Service实例
   service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);
   //创建Service的Context
   ContextImpl context = ContextImpl.getImpl(service.createServiceBaseContext(this, packageInfo));
   //绑定Context到Service,让Service可以访问 Context
   service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
   //调用service的onCreate()方法
   service.onCreate();
   //...通知AMS,Service已经启动完成...
}

可以看到Service的创建过程跟上面的Application、Activity都很类似。

MutableContextWrapper

MutableContextWrapper继承自ContextWrapper,提供了 setBaseContext(Context base) 方法,允许在运行时更改 Context,而不像ContextWrapper那样只能在创建时指定Context。关于MutableContextWrapper的详细介绍后面单独整理一篇。

Context 选择指南

  • Application Context 适用于 全局管理,避免 Activity 相关的 内存泄漏。
  • Activity Context 适用于 界面相关操作(如 Dialog、startActivity())。
  • Service、BroadcastReceiver、ContentProvider 也可以使用 Context,但要 注意生命周期管理。

扩展

getApplicationContext 和 getApplication() 的区别

尽管在当前的 Android Activity 和 Service 实现中,getApplication() 和 getApplicationContext() 返回的是同一个对象,但并不能保证它们始终如此(例如,在某些特定厂商的实现中可能有所不同)。因此,如果想获取 Manifest 中注册的 Application 类,不应该调用 getApplicationContext() 并将其强制转换为Application,因为它可能并不是实际的Application 实例(在测试框架中可能已经遇到过这个问题)。那么 getApplicationContext() 为什么会存在呢?

getApplication() 仅在 Activity 和 Service 类中可用,而 getApplicationContext() 是在 Context 类中声明的。这意味着:

  • 在 BroadcastReceiver 代码中,无法直接调用 getApplication(),因为 BroadcastReceiver 本身不是 Context,它只能通过 onReceive 方法获得 Context。
  • 这也意味着,在 BroadcastReceiver 中无法保证可以访问 Application 实例,因此只能使用 getApplicationContext()。

当查看 Android 代码时,可以发现:

  • Activity 在被 attach 时,会接收一个 baseContext 和 Application,这两个是不同的参数。
  • getApplicationContext() 实际上是委托 baseContext.getApplicationContext() 来获取 Application 实例。

来自stackoverflowstackoverflow.com/questions/5...

相关推荐
tan &26 分钟前
Android开发案例——简单计算器
android
梦想不只是梦与想28 分钟前
鸿蒙系统开发状态更新字段区别对比
android·java·flutter·web·鸿蒙
RichardLai881 小时前
[Flutter学习之Dart基础] - 集合(List, Set,Map)
android·flutter
bst@微胖子1 小时前
Flutter项目之设置页
android·javascript·flutter
杨忆2 小时前
Android 开发 如何生成系统签名
android
我最厉害。,。2 小时前
XSS 跨站&Cookie 盗取&表单劫持&网络钓鱼&溯源分析&项目平台框架
android·网络·xss
百锦再3 小时前
Android ImageView 使用详解
android·java·app·手机·安卓·studio
麦田里的守望者江5 小时前
这个PC项目是去做还是不去做?
android·c++
宁子希5 小时前
如何将 ESP32 快速接入高德、心知、和风天气API 获取天气信息
android·单片机·嵌入式硬件·esp32