Context 的关联类
开发中经常使用的Context 的使用场景大体分为两类:
- 使用Context 调用方法,比如启动 Activity、访问资源、调用系统服务等。
- 调用方法时传入,比如弹出 Toast、创建dialog。
Activity、Service 与 Application 都间接继承 Context,因此可以说一个应用进程的Context 数量 = Activity 数量 + Service 数量 + 1,这个1就是Application数量。Context 的关联类的关系如下图所示:
可以看出,ContextWrapper 中包含有 Context 类型的 mBase 对象,mBase 具体指向 ContextImpl,此外,ContextThemeWrapper、Service 和 Application 都继承自 ContextWrapper,这样它们都能通过 mBase 来使用Context 的方法。同时它们也是装饰类,在 ContextWrapper 上又添加了不少功能。比如,ContextThemeWrapper 包含了主题相关的方法(getTheme之类),因此Activity 继承了ContextThemeWrapper,而Service 不需要主题,因此继承 ContextWrapper。Context 关联类的继承结构有以下优点:
- 使用者能够方便使用Context 的功能。
- 如果 ContextImpl 发生了变化,它的装饰类 ContextWrapper 无需做任何修改。
- ContextImpl 的具体实现不会暴露给使用者。
- 通过组合而不是继承,拓展 ContextImpl 的功能。运行时选择不同的装饰类,实现不同功能。
Application Context 的创建过程
我们通过 getApplicationContext 来获取应用程序的全局 Application Context,那么 Application Context 是如何创建的呢?在应用程序启动完成后,应用程序就有一个全局的 Application Context,那就从应用程序启动过程着手,Application Context 的创建过程时序图如下:
应用程序进程的主线程管理类 ActivityThread 会调用内部类 ApplicationThread 的 scheduleLaunchActivity 方法来启动Activity,而在scheduleLaunchActivity 中会向 H 发送 LAUNCH_ACTIVITY 类型消息,目的是将启动Activity 的逻辑放在主线程中。在 H 的 handleMessage 方法中最终会调用到 LoadApk 类的 makeApplication 方法:
1 | //frameworks/base/core/java/android/app/LoadedApk.java |
注释1处,假设是第一次启动应用程序,因此 mApplication 为null,在注释2处通过 Contextimpl 的 createAppContext 方法创建 Contextimpl 的实例,注释3中创建了 Application 对象,注释4处将 Application 对象赋值给 Contextimpl 的成员变量 mOuterContext ,这样,ContextImpl 中也包含了 Application 的引用。注释5处的 mApplication 即 LoadedApk 的成员变量 mApplication。来看看注释 3 处Application 是如何创建的(最终调用到如下代码的方法):
1 | //frameworks/base/core/java/android/app/lnstrumentation.java |
注释1处通过反射来创建Application,并调用其 attach 方法,并且将 ContextImpl 类型的对象传进去:
1 | //frameworks/base/core/java/android/app/Application.java |
最终,把一路传过来的 ContextImpl 类型的 base 赋值给 Application 的 mBase 。前面讲过,这个歌mBase 是ContextWrapper 的成员变量,因为Application 继承 ContextWrapper ,所以才有这个变量。因此,Application 的attach 方法的作用就是使 Application 可以使用 Context 的方法,这样,Application 才可以用来代表 Application Context。
Application Context 的获取过程
我们通过 getApplicationContext 来获取Application Context,这个方法在 ContextWrapper 中实现:
1 | //frameworks/base/core/java/android/content/ContextWrapper.java |
从前面我们可知,mBase 指的是 ComtextImpl,具体代码:
1 | //frameworks/base/core/java/android/app/ContextImpl.java |
如果 loadedApk 类型的mPackageinfo 不为 null,则调用其 getApplication 方法,否则调用 ActivityThread 的 getApplication 方法:
1 | //frameworks/base/core/java/android/app/LoadedApk.java |
这个 mApplication 应该熟悉,是在前面提到的 LoadedApk 的 makeApplication 方法中注释 5 处被赋值的,是个Application 对象。就这样,我们获取到 Application Context。
Activity 的Context 创建过程
应用程序进程的主线程管理类 ActivityThread 会调用内部类 ApplicationThread 的 scheduleLaunchActivity 方法来启动Activity,最终通过 H 类在主线程中处理启动事项,最终调用到 ActivityThread 的 performLaunchActivity 方法:
1 | //frameworks/base/core/java/android/app/ActivityThread.java |
在注释2处创建了Activity 的实例,注释1处通过 createBaseContextForActivity 方法创建 Activity 的 ContextImpl,并在注释4 处将 ContextImpl 对象传给activity 的attach方法,在注释3处调用了 ContextImpl 的 setOuterContext 方法,将 Activity 的实例赋值给 ContextImpl 的成员变量 mOuterContext ,这样,ContextImpl 也可以访问 Activity 的变量和方法。createBaseContextForActivity 方法中,最终也会调用 ContextWrapper 的 attachBaseContext ,将Activity 中的 ContextImpl 对象赋值给 ContextWrapper 的成员变量 mBase,这样,ContextWrapper 的功能就可以交由 ContextImpl 来处理。
Service 的 Context 创建过程
Service 的Context 创建过程与 Activity 的Context 创建过程类似,这里先略过,后续有时间再记录。