理解上下文 Context

Context 也就是上下文对象,是 Android 常用的类,四大组件都会涉及 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 等。

  • ContextImpl 和 ContextWrapper 继承自 Context。
  • ContextWrapper 内部包含 Context 类型的 mBase 对象,mBase 具体指向 ContextImpl。
  • ContextImpl 提供了很多功能,但是外界需要使用并拓展 ContextImpl 的功能,因此设计上使用了装饰模式,ContextWrapper 是装饰类,它对 ContextImpl 进行包装,ContextWrapper 主要是起了方法传递的作用,ContextWrapper 中几乎所有的方法都是调用 ContextImpl 的相应方法来实现的。
  • ContextThemeWrapper、Service 和 Application 都继承自 ContextWrapper,这样它们都可以通过 mBase 来使用 Context 的方法,同时它们也是装饰类,在 ContextWrapper 的基础上又添加了不同的功能。
  • ContextThemeWrapper 中包含和主题相关的方法(比如 getTheme 方法),因此,需要主题的 Activity 继承 ContextThemeWrapper,而不需要主题的 Service 继承 ContextWrapper。

Context 的关联类采用了装饰模式,主要有以下的优点:

  • 使用者(比如 Service)能够更方便地使用 Context。
  • 如果 ContextImpl 发生了变化,它的装饰类 ContextWrapper 不需要做任何修改。
  • ContextImpl 的实现不会暴露给使用者,使用者也不必关心 ContextImpl 的实现。
  • 通过组合而非继承的方式,拓展 ContextImpl 的功能,在运行时选择不同的装饰类,实现不同的功能。

为了更好地理解 Context 的关联类的设计理念,接下来分别介绍 Application 、Activity、Service 的 Context 的创建过程。


Application Context 的创建过程

在一个应用程序启动完成后,应用程序就会有一个全局的 Application Context,并且我们可以通过调用 getApplicationContext 来获取。接下来就从应用程序启动过程开始着手分析 Application Context 的创建过程。

ActivityThread 类作为应用程序进程的主线程管理类,它会调用它的内部类 ApplicationThread 的 scheduleLaunchActivity() 启动 Activity,ActivityThread.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
...
updatePendingConfiguration(curConfig);
// 向 H 类发送 LAUNCH_ACTIVITY 类型的消息,目的是将启动 Activity 的逻辑放在主线程的消息队列中,这样启动 Activity 的逻辑会在主线程中执行。
sendMessage(H.LAUNCH_ACTIVITY, r);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// H 继承自 Handler,是 ActivityThread 的内部类。  
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;
// 通过 getPackageInfoNoCheck 方法获得 LoadedApk 类型的对象,并将该对象赋值给 ActivityClientRecord 的成员变量 packageInfo,其中 LoadedApk 用来描述已加载的 APK 文件。
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
// 调用了 ActivityThread 的 handleLaunchActivity 方法。
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
}

在 handleLaunchActivity 方法中调用了 ActivityThread 的 performLaunchActivity 方法,

1
2
3
4
5
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...
Activity a = performLaunchActivity(r, customIntent);
...
}
1
2
3
4
5
6
7
8
9
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
try {
// ActivityClientRecord 的成员变量 packageInfo 是 LoadedApk 类型的。
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
}
...
}

performLaunchActivity 方法中有很多重要的逻辑,这里只保留了和 Application Context 相关的逻辑,接下来查看 LoadedApk 的 makeApplication 方法,LoadedApk.java,做了如下几件事:

  • 判断 mApplication 不为 null 则返回 mApplication,这里假设是第一次启动应用程序,因此 mApplication 为null。

    1
    2
    3
    4
    5
    private Application mApplication;
    ...
    if (mApplication != null) {
    return mApplication;
    }
  • 通过 ContextImpl 的 createAppContext 方法来创建 ContextImpl。

    1
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
  • 创建 Application,在 Instrumentation 的 newApplication 方法中传入了 ClassLoader 类型的对象以及上一步创建的 ContextImpl 。

    1
    2
    3
    Application app = null;
    ...
    app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
  • 将 Application 赋值给 ContextImpl 的 Context 类型的成员变量 mOuterContext,这样 ContextImpl 中也包含了 Application 的引用。

    1
    appContext.setOuterContext(app);
  • 将 Application 赋值给 LoadedApk 的成员变量 mApplication,这个 mApplication 是 Application 类型的对象,它用来代表 Application Context,在 Application Context 的获取过程中会再次提到 mApplication。

    1
    mApplication = app;

接下来查看 Instrumentation 的 newApplication 方法,看下 Application 是如何创建的,Instrumentation.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}

/**
* 有两个 newApplication 重载方法,最终会调用这个重载方法。
*/
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
// 通过反射来创建 Application。
Application app = (Application)clazz.newInstance();
// 调用 Application 的 attach 方法,将 ContextImpl 传进去。
app.attach(context);
// 返回 Application。
return app;
}

在 Application 的 attach 方法中调用了 attachBaseContext 方法,Application.java

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

attachBaseContext 方法在 Application 的父类 ContextWrapper 中实现如下, ContextWrapper.java

1
2
3
4
5
6
7
8
9
10
/**
* 这个一路传递过来的 base 指的是 ContextImpl,它是 Context 的实现类。
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
// 将 ContextImpl 赋值给 ContextWrapper 的 Context 类型的成员变量 mBase,这样 ContextWrapper 中就可以使用 Context 的方法,而 Application 继承自 ContextWrapper,同样可以使用 Context 的方法。
mBase = base;
}

Application 的 attach 方法的作用就是使 Application 可以使用 Context 的方法,这样 Application 才可以用来代表 Application Context。

Application Context 的获取过程

我们是通过调用 getApplicationContext 方法来获得 Application Context,此方法在 ContextWrapper 中实现。ContextWrapper.java

1
2
3
4
5
@Override
public Context getApplicationContext() {
// mBase 指的是 ContextImpl
return mBase.getApplicationContext();
}

查看 ContextImpl 的 getApplicationContext()。ContextImpl.java

1
2
3
4
5
6
7
@Override
public Context getApplicationContext() {
// 如果 LoadedApk 类型的 mPackageInfo 不为 null,则调用它的 getApplication 方法,否则调用 ActivityThread 的 getApplication 方法。
// 由于应用程序这时已经启动,因此 LoadedApk 不会为 null,则会调用 LoadedApk 的 getApplication 方法。
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}

LoadedApk 的 getApplication 方法。LoadedApk.java

1
2
3
4
Application getApplication() {
// mApplication 在 LoadedApk 的 makeApplication 方法中被赋值。
return mApplication;
}

这样,便通过 getApplicationContext 方法获取到了 Application Context。

Activity 的 Context 创建过程

想要在 Activity 中使用 Context 提供的方法,务必要先创建 Context,Activity 的 Context 会在 Activity 的启动过程中被创建。

ActivityThread 是应用程序进程的主线程管理类,它的内部类 ApplicationThread 会调用 scheduleLaunchActivity 方法启动 Activity,ActivityThread.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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);
// 将启动 Activity 的参数封装成 ActivityClientRecord
ActivityClientRecord r = new ActivityClientRecord();

r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
...
// 向 H 类发送类型为 LAUNCH_ACTIVITY 的消息,并将 ActivityClientRecord 传递过去。
sendMessage(H.LAUNCH_ACTIVITY, r);
}

H 类的 handleMessage 方法会对此类型的消息进行处理,其中调用了 ActivityThread 的 handleLaunchActivity 方法,此方法又调用了 performLaunchActivity 方法,此方法中有很多重要的逻辑,这里只保留 Activity 的 Context 相关的逻辑。 ActivityThread.java

  • 创建 Activity 的 ContextImpl

    1
    2
    // 在 createBaseContextForActivity 方法中会调用 ContextImpl 的 createActivityContext 方法来创建 ContextImpl。
    ContextImpl appContext = createBaseContextForActivity(r);
  • 创建 Activity 的实例

    1
    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
  • 将创建的 Activity 实例赋值给 ContextImpl 的成员变量 mOuterContext,这样 ContextImpl 也可以访问 Activity 的变量和方法。

    1
    appContext.setOuterContext(activity);
  • 将创建的 ContextImpl 传入 activity 的 attach 方法中。

    1
    2
    3
    4
    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);
  • 在 mInstrumentation 的 callActivityOnCreate 方法中会调用 Activity 的 onCreate 方法。

    1
    2
    3
    4
    5
    if (r.isPersistable()) {
    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
    mInstrumentation.callActivityOnCreate(activity, r.state);
    }

接下来查看 Activity 的 attach 方法。Activity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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) {
// attachBaseContext 方法在 ContextThemeWrapper 中实现,此方法内部调用了 ContextThemeWrapper 的父类 ContextWrapper 的 attachBaseContext 方法。(super.attachBaseContext(newBase))
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
// 创建 PhoneWindow,它代表应用程序窗口。
// PhoneWindow 在运行中会间接触发很多事件,比如点击、菜单弹出、屏幕焦点变化等事件,这些事件需要转发给与 PhoneWindow 关联的 Activity,转发操作通过 Window.Callback 接口实现,Activity 实现了这个接口。
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
// 将当前 Activity 通过 Window 的 setCallback 方法传递给 PhoneWindow。
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
// 为 PhoneWindow 设置 WindowManager。
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
// 获取 WindowManager 并赋值给 Activity 的成员变量 mWindowManager。
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;

mWindow.setColorMode(info.colorMode);
}

接下来查看 ContextWrapper 的 attachBaseContext 方法 。ContextWrapper.java

1
2
3
4
5
6
7
8
9
/**
* base 指的是一路传递过来的 Activity 的 ContextImpl,将它赋值给 ContextWrapper 的成员变量 mBase。这样 ContextWrapper 的功能就可以交由 ContextImpl 来处理。
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}

举个例子:ContextWrapper.java

1
2
3
4
5
6
7
/**
* 当调用 getTheme 方法时,实际是调用了 ContextImpl 的 getTheme 方法。
*/
@Override
public Resources.Theme getTheme() {
return mBase.getTheme();
}

总结:在启动 Activity 的过程中创建 ContextImpl,并赋值给 ContextWrapper 的成员变量 mBase。Activity 继承自 ContextWrapper 的子类 ContextThemeWrapper,这样在 Activity 中就可以使用 Context 中定义的方法了。

Service 的 Context 创建过程

与 Activity 的过程类似,是在 Service 的启动过程中被创建的。

ActivityThread 的内部类 ApplicationThread 会调用 scheduleCreateService 方法启动 Service,ActivityThread.java

1
2
3
4
5
6
7
8
9
10
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;

sendMessage(H.CREATE_SERVICE, s);
}

向 H 类发送 CREATE_SERVICE 类型消息,H 类的 handleMessage 方法会对此类型消息进行处理,其中调用了 ActivityThread 的 handleCreateService 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void handleCreateService(CreateServiceData data) {
...
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
// 创建 ContextImpl。
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);

Application app = packageInfo.makeApplication(false, mInstrumentation);
// 将创建的 ContextImpl 传入到 service 的 attach 方法中。
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
...
}
}

接下来查看 service 的 attach 方法。Service.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
// 调用了 ContextWrapper 的 attachBaseContext 方法。
attachBaseContext(context);
mThread = thread; // NOTE: unused - remove?
mClassName = className;
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}

接下来查看 ContextWrapper 的 attachBaseContext 方法 。ContextWrapper.java

1
2
3
4
5
6
7
8
9
/**
* base 就是一路传递过来的 ContextImpl,将它赋值给 ContextWrapper 的 Context 类型的成员变量 mBase。这样 ContextWrapper 中就可以使用 Context 的方法。而 Service 继承自 ContextWrapper,同样可以使用 Context 的方法。
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}

备注

注:基于 Android 8.0 版本源码分析

参考资料:
Android 进阶解密

单词音标: