Android深刻理解Context(一)Context关联类和Application Context建立过程

相关文章
Android深刻四大组件系列html

前言

Context也就是上下文对象,是Android较为经常使用的类,可是对于Context,不少人都停留在会用的阶段,这个系列会带你们从源码角度来分析Context,从而更加深刻的理解它。java

1.Context概述

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

  • 使用Context调用方法,好比:启动Activity、访问资源、调用系统级服务等。
  • 调用方法时传入Context,好比:弹出Toast、建立Dialog等。

Activity、Service和Application都是间接的继承自Context的,所以,咱们能够计算出一个应用程序进程中有多少个Context,这个数量等于Activity和Service的总个数加1,1指的是Application的数量。微信

Context是一个抽象类,它的内部定义了不少方法以及静态常量,它的具体实现类为ContextImpl。和Context相关联的类,除了ContextImpl还有ContextWrapper、ContextThemeWrapper和Activity等等,下面给出Context的关系图。
app

未命名文件(1).png

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

2.Application Context的建立过程

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

Android深刻四大组件(一)应用程序启动过程(后篇)这篇文章的最后讲了ActivityThread启动Activity。ActivityThread做为应用程序进程的核心类,它会调用它的内部类ApplicationThread的scheduleLaunchActivity方法来启动Activity,以下所示。spa

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类型的消息的处理。code

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相关的逻辑,想要更多了解performLaunchActivity方法中的逻辑请查看Android深刻四大组件(一)应用程序启动过程(后篇)这篇文章的第二小节。这里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。Application Context的建立过程就讲到这里,最后给出Application Context建立过程的时序图。

3.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。


欢迎关注个人微信公众号,第一时间得到博客更新提醒,以及更多成体系的Android相关原创技术干货。
扫一扫下方二维码或者长按识别二维码,便可关注。

相关文章
相关标签/搜索