理解上下文 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 |
|
1 | // H 继承自 Handler,是 ActivityThread 的内部类。 |
在 handleLaunchActivity 方法中调用了 ActivityThread 的 performLaunchActivity 方法,
1 | private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { |
1 | private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { |
performLaunchActivity 方法中有很多重要的逻辑,这里只保留了和 Application Context 相关的逻辑,接下来查看 LoadedApk 的 makeApplication 方法,LoadedApk.java,做了如下几件事:
判断 mApplication 不为 null 则返回 mApplication,这里假设是第一次启动应用程序,因此 mApplication 为null。
1
2
3
4
5private Application mApplication;
...
if (mApplication != null) {
return mApplication;
}通过 ContextImpl 的 createAppContext 方法来创建 ContextImpl。
1
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
创建 Application,在 Instrumentation 的 newApplication 方法中传入了 ClassLoader 类型的对象以及上一步创建的 ContextImpl 。
1
2
3Application 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 | public Application newApplication(ClassLoader cl, String className, Context context) |
在 Application 的 attach 方法中调用了 attachBaseContext 方法,Application.java
1 | /* package */ final void attach(Context context) { |
attachBaseContext 方法在 Application 的父类 ContextWrapper 中实现如下, ContextWrapper.java
1 | /** |
Application 的 attach 方法的作用就是使 Application 可以使用 Context 的方法,这样 Application 才可以用来代表 Application Context。
Application Context 的获取过程
我们是通过调用 getApplicationContext 方法来获得 Application Context,此方法在 ContextWrapper 中实现。ContextWrapper.java
1 |
|
查看 ContextImpl 的 getApplicationContext()。ContextImpl.java
1 |
|
LoadedApk 的 getApplication 方法。LoadedApk.java
1 | Application getApplication() { |
这样,便通过 getApplicationContext 方法获取到了 Application Context。
Activity 的 Context 创建过程
想要在 Activity 中使用 Context 提供的方法,务必要先创建 Context,Activity 的 Context 会在 Activity 的启动过程中被创建。
ActivityThread 是应用程序进程的主线程管理类,它的内部类 ApplicationThread 会调用 scheduleLaunchActivity 方法启动 Activity,ActivityThread.java
1 |
|
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
4activity.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
5if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
接下来查看 Activity 的 attach 方法。Activity.java
1 | final void attach(Context context, ActivityThread aThread, |
接下来查看 ContextWrapper 的 attachBaseContext 方法 。ContextWrapper.java
1 | /** |
举个例子:ContextWrapper.java
1 | /** |
总结:在启动 Activity 的过程中创建 ContextImpl,并赋值给 ContextWrapper 的成员变量 mBase。Activity 继承自 ContextWrapper 的子类 ContextThemeWrapper,这样在 Activity 中就可以使用 Context 中定义的方法了。
Service 的 Context 创建过程
与 Activity 的过程类似,是在 Service 的启动过程中被创建的。
ActivityThread 的内部类 ApplicationThread 会调用 scheduleCreateService 方法启动 Service,ActivityThread.java
1 | public final void scheduleCreateService(IBinder token, |
向 H 类发送 CREATE_SERVICE 类型消息,H 类的 handleMessage 方法会对此类型消息进行处理,其中调用了 ActivityThread 的 handleCreateService 方法。
1 | private void handleCreateService(CreateServiceData data) { |
接下来查看 service 的 attach 方法。Service.java
1 | public final void attach( |
接下来查看 ContextWrapper 的 attachBaseContext 方法 。ContextWrapper.java
1 | /** |
备注
注:基于 Android 8.0 版本源码分析
参考资料:
Android 进阶解密