安卓Context上下文


前言

Context也就是上下文对象,是Android较为常用的类,但是对于Context,大多都停留在会用的阶段,本文会从源码角度来分析Context,从而更加深入的理解它。

一、Context简介

Context意为上下文或者场景,是一个应用程序环境信息的接口。
在开发中我们经常会使用Context,它的使用场景总的来说分为两大类,它们分别是:

  • 使用Context调用方法,比如:启动Activity、访问资源、调用系统级服务等。
  • 调用方法时传入Context,比如:弹出Toast、创建Dialog等。
    Activity、Service和Application都是间接的继承自Context的,因此,可以计算出一个应用程序进程中有多少个Context,这个数量等于Activity和Service的总个数加1,1指的是Application的数量。

Context是一个抽象类,它的内部定义了很多方法以及静态常量,它的具体实现类为ContextImpl。和Context相关联的类,除了ContextImpl还有ContextWrapper、ContextThemeWrapper和Activity等等,下面给出Context的关系图。
在这里插入图片描述

从图中我们可以看出,ContextImpl和ContextWrapper继承自Context,ContextThemeWrapper、Service和Application继承自ContextWrapper。ContextWrapper和ContextThemeWrapper都是Context的包装类,它们都含有Context类型的mBase对象,mBase具体指向的是ContextImpl,这样通过ContextWrapper和ContextThemeWrapper也可以使用Context的方法。ContextThemeWrapper中包含和主题相关的方法(比如: getTheme方法),因此,需要主题的Activity继承ContextThemeWrapper,而不需要主题的Service则继承ContextWrapper。


二、Application Context

2.1 Application Context的创建过程

我们通过调用getApplicationContext来获取应用程序的全局的Application Context,那么Application Context是如何创建的呢?
当一个应用程序启动完成后,应用程序就会有一个全局的Application Context。那么我们就从应用程序启动过程开始着手。

ActivityThread作为应用程序进程的核心类,它会调用它的内部类ApplicationThread的scheduleLaunchActivity方法来启动Activity,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

    private class ApplicationThread extends ApplicationThreadNative {
        
     ...
       @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
                updateProcessState(procState, false);
                ActivityClientRecord r = new ActivityClientRecord();
                ...
                sendMessage(H.LAUNCH_ACTIVITY, r);
        }
     ...   
    }    

在ApplicationThread的scheduleLaunchActivity方法中向H类发送LAUNCH_ACTIVITY类型的消息,目的是将启动Activity的逻辑放在主线程中的消息队列中,这样启动Activity的逻辑会在主线程中执行。我们接着查看H类的handleMessage方法对LAUNCH_ACTIVITY类型的消息的处理。

frameworks/base/core/java/android/app/ActivityThread.java

private class H extends Handler {
        
      public static final int LAUNCH_ACTIVITY         = 100;
...
public void handleMessage(Message msg) {
          if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
          switch (msg.what) {
              case LAUNCH_ACTIVITY: {
                  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                  final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                  r.packageInfo = getPackageInfoNoCheck(
                          r.activityInfo.applicationInfo, r.compatInfo);//1
                  handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");//2
                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              } break;

            ...
}

H继承自Handler ,是ActivityThread的内部类。在注释1处通过getPackageInfoNoCheck方法获得LoadedApk类型的对象,并将该对象赋值给ActivityClientRecord 的成员变量packageInfo,其中LoadedApk用来描述已加载的APK文件。在注释2处调用handleLaunchActivity方法,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

  private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
       ...
        Activity a = performLaunchActivity(r, customIntent);
       ...
    }

接着查看performLaunchActivity方法:
frameworks/base/core/java/android/app/ActivityThread.java

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
           ...
        } 
        ...
        return activity;
    }

performLaunchActivity方法中有很多重要的逻辑,这里只保留了Application Context相关的逻辑,这里ActivityClientRecord 的成员变量packageInfo是LoadedApk类型的,接着来查看LoadedApk的makeApplication方法,如下所示。

frameworks/base/core/java/android/app/LoadedApk.java

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
       //1
        return mApplication;
    }
    ...
    try {
      ...
       java.lang.ClassLoader cl = getClassLoader();
      ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//2
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);//3
        appContext.setOuterContext(app);//4
    } catch (Exception e) {
       ...
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;//5
    ...
    return app;
}

注释1处如果mApplication不为null则返回mApplication,这里假设是第一次启动应用程序,因此mApplication为null。
注释2处通过ContextImpl的createAppContext方法来创建ContextImpl。
注释3处的代码用来创建Application,在Instrumentation的newApplication方法中传入了ClassLoader类型的对象以及注释2处创建的ContextImpl 。
注释4处将Application赋值给ContextImpl的Context类型的成员变量mOuterContext。
注释5处将Application赋值给LoadedApk的成员变量mApplication,在Application Context的获取过程中我们会再次用到mApplication。
来查看注释3处的Application是如何创建的,Instrumentation的newApplication方法如下所示。
frameworks/base/core/java/android/app/Instrumentation.java

static public Application newApplication(Class<?> clazz, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = (Application)clazz.newInstance();//1
    app.attach(context);
    return app;
}

Instrumentation中有两个newApplication重载方法,最终会调用上面这个重载方法。注释1处通过反射来创建Application,并调用了Application的attach方法,并将ContextImpl传进去:
frameworks/base/core/java/android/app/Application.java

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

attach方法中调用了attachBaseContext方法,它的实现在Application的父类ContextWrapper中,代码如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

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

从上文得知,这个base指的是ContextImpl,将ContextImpl赋值给ContextWrapper的Context类型的成员变量mBase。


2.2 Application Context的获取过程

熟知了Application Context的创建过程,那么它的获取过程会非常好理解。我们通过调用getApplicationContext方法来获得Application Context,getApplicationContext方法的实现在ContextWrapper中,如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

从上文得知,mBase指的是ContextImpl,我们来查看 ContextImpl的getApplicationContext方法:
frameworks/base/core/java/android/app/ContextImpl.java

Override
public Context getApplicationContext() {
    return (mPackageInfo != null) ?
            mPackageInfo.getApplication() : mMainThread.getApplication();
}

如果LoadedApk不为null,则调用LoadedApk的getApplication方法,否则调用AvtivityThread的getApplication方法。由于应用程序这时已经启动,因此LoadedApk不会为null,则会调用LoadedApk的getApplication方法:
frameworks/base/core/java/android/app/LoadedApk.java

   Application getApplication() {
        return mApplication;
    }

这里的mApplication我们应该很熟悉,它在上文LoadedApk的makeApplication方法的注释5处被赋值。这样我们通过getApplicationContext方法就获取到了Application Context。


三、Activity的Context创建过程

当我们在Activity中调用startActivity方法时,其实调用的是Context的startActivity方法,如果想要在Activity中使用Context提供的方法,务必要先创建Context。Activity的Context会在Activity的启动过程中被创建, ActivityThread是应用程序进程的核心类,它的内部类ApplicationThread会调用scheduleLaunchActivity方法来启动Activity,scheduleLaunchActivity方法如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
        updateProcessState(procState, false);
        ActivityClientRecord r = new ActivityClientRecord();
        r.token = token;
        ...
        sendMessage(H.LAUNCH_ACTIVITY, r);
}

scheduleLaunchActivity方法会将启动Activity的参数封装成ActivityClientRecord ,sendMessage方法向H类发送类型为LAUNCH_ACTIVITY的消息,并将ActivityClientRecord 传递过去。sendMessage方法的目的是将启动Activity的逻辑放在主线程中的消息队列中,这样启动Activity的逻辑就会在主线程中执行。
H类的handleMessage方法中会对LAUNCH_ACTIVITY类型的消息进行处理,其中调用了handleLaunchActivity方法,而handleLaunchActivity方法中又调用performLaunchActivity方法,来查看performLaunchActivity方法。
frameworks/base/core/java/android/app/ActivityThread.java

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);//1
             ...
            }
        } catch (Exception e) {
           ...
        }

        try {
          ...
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);//2
                ...
                /**
                *3
                */
                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); 
                ...
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);//4
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
               ...
        }

        return activity;
    }

performLaunchActivity方法中有很多重要的逻辑,这里只保留了Activity的Context相关的逻辑。在注释1处用来创建Activity的实例。注释2处通过createBaseContextForActivity方法用来创建Activity的ContextImpl,并将ContextImpl传入注释3处的activity的attach方法中。在注释4处Instrumentation的callActivityOnCreate方法中会调用Activity的onCreate方法。
我们先来查看注释2出的createBaseContextForActivity方法:

frameworks/base/core/java/android/app/ActivityThread.java

 private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        ...
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.token, displayId, r.overrideConfig);//1
        appContext.setOuterContext(activity);//2
        Context baseContext = appContext;
        ...
        return baseContext;
    }

在注释1处调用ContextImpl的createActivityContext方法来创建ContextImpl,注释2处调用了ContextImpl的setOuterContext方法,将此前创建的Activity 实例赋值给ContextImpl的成员变量mOuterContext,这样ContextImpl也可以访问Activity的变量和方法。
我们再回到ActivityThread的performLaunchActivity方法,查看注释3处的Activity的attach方法,如下所示。
frameworks/base/core/java/android/app/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) {
        attachBaseContext(context);//1
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window);//2
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);//3
        mWindow.setOnWindowDismissedCallback(this);
        ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//4
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();//5
        mCurrentConfig = config;
    }

在注释2处创建PhoneWindow,它代表应用程序窗口。PhoneWindow在运行中会间接触发很多事件,比如点击事件、菜单弹出、屏幕焦点变化等事件,这些事件需要转发给与PhoneWindow关联的Actvity,转发操作通过Window.Callback接口实现,Actvity实现了这个接口,在注释3处将当前Activity通过Window的setCallback方法传递给PhoneWindow。
注释4处给PhoneWindow设置WindowManager,并在注释5处获取WindowManager并赋值给Activity的成员变量mWindowManager ,这样在Activity中就可以通过getWindowManager方法来获取WindowManager。
在注释1处调用了ContextThemeWrapper的attachBaseContext方法,如下所示。

frameworks/base/core/java/android/view/ContextThemeWrapper.java

Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(newBase);
}

attachBaseContext方法接着调用ContextThemeWrapper的父类ContextWrapper的attachBaseContext方法:

frameworks/base/core/java/android/content/ContextWrapper.java

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

注释1处的base指的是一路传递过来的Activity的ContextImpl,将它赋值给ContextWrapper的成员变量mBase。这样ContextWrapper的功能就可以交由ContextImpl处理,举个例子:
frameworks/base/core/java/android/content/ContextWrapper.java

@Override
public Resources.Theme getTheme() {
    return mBase.getTheme();
}

当调用ContextWrapper的getTheme方法,其实就是调用的ContextImpl的getTheme方法。
Activity的Context创建过程就讲到这里。 总结一下,在启动Activity的过程中创建ContextImpl,并赋值给ContextWrapper的成员变量mBase中。Activity继承自ContextWrapper的子类ContextThemeWrapper,这样在Activity中就可以使用ContextImpl了。


四、Service的Context创建过程

Service的Context创建过程与Activity的Context创建过程类似,也是在Service的启动过程中被创建 ActivityThread的内部类ApplicationThread会调用scheduleCreateService方法来启动Service,如下所示。
frameworks/base/core/java/android/app/ActivityThread.java

public final void scheduleCreateService(IBinder token,
         ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
     ...
     sendMessage(H.CREATE_SERVICE, s);
 }

sendMessage方法向H类发送CREATE_SERVICE类型的消息,H类的handleMessage方法中会对CREATE_SERVICE类型的消息进行处理,其中调用了handleCreateService方法:
frameworks/base/core/java/android/app/ActivityThread.java

 private void handleCreateService(CreateServiceData data) {
      ...
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);//1
            context.setOuterContext(service);
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());//2
            service.onCreate();
          ...
        } catch (Exception e) {
          ... 
        }
    }

在注释1处创建了ContextImpl ,并将该ContextImpl传入注释2处service的attach方法中:
frameworks/base/core/java/android/app/Service.java

 public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        attachBaseContext(context);//1
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

注释1处调用了ContextWrapper的attachBaseContext方法。
frameworks/base/core/java/android/content/ContextWrapper.java

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

attachBaseContext方法在前文已经讲过,这里不再赘述。
Service的Context创建过程就讲解到这里,它和Activity的Context创建过程类似。


参考链接:
深度详解 Android 之 Context
Android Context完全解析,你所不知道的Context的各种细节

相关推荐

  1. 下Application和Activity的Context获取

    2024-06-18 07:20:02       33 阅读
  2. day 2| 上下文Context

    2024-06-18 07:20:02       16 阅读
  3. go标准库Context上下文

    2024-06-18 07:20:02       31 阅读
  4. Go Context -- 管理请求的上下文信息

    2024-06-18 07:20:02       28 阅读
  5. BFC(Block Formatting Contexts)块级格式化上下文

    2024-06-18 07:20:02       34 阅读
  6. go context.todo生成默认空的上下文

    2024-06-18 07:20:02       27 阅读

最近更新

  1. AWS EKS上GPU工作负载自动扩缩容的异常排查指南

    2024-06-18 07:20:02       0 阅读
  2. 深入WebKit内核:揭秘HTML与XML的识别之谜

    2024-06-18 07:20:02       0 阅读
  3. shell脚本实现mysql 数据库备份

    2024-06-18 07:20:02       1 阅读
  4. 数据结构第11节: B树

    2024-06-18 07:20:02       1 阅读
  5. Spring Boot与RSocket的集成

    2024-06-18 07:20:02       1 阅读
  6. 责任链模式

    2024-06-18 07:20:02       1 阅读

热门阅读

  1. 【神经网络】深度神经网络如何应用于推荐系统

    2024-06-18 07:20:02       6 阅读
  2. TransformerConv

    2024-06-18 07:20:02       7 阅读
  3. 网络安全筑基篇——文件上传

    2024-06-18 07:20:02       8 阅读
  4. c++日期类的实现

    2024-06-18 07:20:02       7 阅读
  5. 计算机组成原理-期末考前常见简答题总结

    2024-06-18 07:20:02       8 阅读
  6. TensorFlow音频分类修复

    2024-06-18 07:20:02       7 阅读
  7. 在历史课堂教学过程中培养学生的计算思维能力

    2024-06-18 07:20:02       8 阅读
  8. 【C#基础】C#中的IEnumerable<T>接口介绍

    2024-06-18 07:20:02       7 阅读
  9. 企业级-pdf分页数据推送接收解析保存

    2024-06-18 07:20:02       8 阅读
  10. [absl_py][python]absl_py所有whl文件下载地址汇总

    2024-06-18 07:20:02       8 阅读
  11. XML 应用程序

    2024-06-18 07:20:02       6 阅读
  12. C#语言进阶(一)—委托 第二篇

    2024-06-18 07:20:02       9 阅读
  13. 数实融合创新发展 隆道分享企业级AI应用

    2024-06-18 07:20:02       8 阅读