0%

第5章:理解上下文Context

Context 的关联类

开发中经常使用的Context 的使用场景大体分为两类:

  • 使用Context 调用方法,比如启动 Activity、访问资源、调用系统服务等。
  • 调用方法时传入,比如弹出 Toast、创建dialog。

Activity、Service 与 Application 都间接继承 Context,因此可以说一个应用进程的Context 数量 = Activity 数量 + Service 数量 + 1,这个1就是Application数量。Context 的关联类的关系如下图所示:

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 的创建过程时序图如下:

Application context 的创建时序图

应用程序进程的主线程管理类 ActivityThread 会调用内部类 ApplicationThread 的 scheduleLaunchActivity 方法来启动Activity,而在scheduleLaunchActivity 中会向 H 发送 LAUNCH_ACTIVITY 类型消息,目的是将启动Activity 的逻辑放在主线程中。在 H 的 handleMessage 方法中最终会调用到 LoadApk 类的 makeApplication 方法:

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
//frameworks/base/core/java/android/app/LoadedApk.java

public Application makeApplication(boolean forceDefaultAppClass,Instrumentation nstrumentation) {
if (mApplication != null) {//l
return mApplication;
}
...
Application app = null;
String appClass = mApplicationinfo.className;
if (forceDefaultAppClass || (appClass == null) ) {
appClass = "android.app.Application”;
}
try {
java.lang.ClassLoader cl= getClassLoader();
if (!mPackageName.equals ("android")){
initializeJavaContextClassLoader ();
}

Contextimpl appContext = Contextimpl.createAppContext(mActivityThread,this); //2
app = mActivityThread.rninstrumentation.newApplication(cl, appClass, appContext);//3
appContext. setOUterContext(app) ;//4
}catch (Exception e){
...
}
...
mApplication = app;//5
...
return app;

}

注释1处,假设是第一次启动应用程序,因此 mApplication 为null,在注释2处通过 Contextimpl 的 createAppContext 方法创建 Contextimpl 的实例,注释3中创建了 Application 对象,注释4处将 Application 对象赋值给 Contextimpl 的成员变量 mOuterContext ,这样,ContextImpl 中也包含了 Application 的引用。注释5处的 mApplication 即 LoadedApk 的成员变量 mApplication。来看看注释 3 处Application 是如何创建的(最终调用到如下代码的方法):

1
2
3
4
5
6
7
//frameworks/base/core/java/android/app/lnstrumentation.java

static public Application newApplication(xxxx){
Application app = (Application) clazz.newinstance ();
app.attach(context) ; //l
return app ;
}

注释1处通过反射来创建Application,并调用其 attach 方法,并且将 ContextImpl 类型的对象传进去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//frameworks/base/core/java/android/app/Application.java

final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = Contextimpl.getimpl(context).mPackageinfo;
}


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

最终,把一路传过来的 ContextImpl 类型的 base 赋值给 Application 的 mBase 。前面讲过,这个歌mBase 是ContextWrapper 的成员变量,因为Application 继承 ContextWrapper ,所以才有这个变量。因此,Application 的attach 方法的作用就是使 Application 可以使用 Context 的方法,这样,Application 才可以用来代表 Application Context。

Application Context 的获取过程

我们通过 getApplicationContext 来获取Application Context,这个方法在 ContextWrapper 中实现:

1
2
3
4
5
//frameworks/base/core/java/android/content/ContextWrapper.java

public Context getApplicationContext()(
return mBase.getApplicationContext( );
}

从前面我们可知,mBase 指的是 ComtextImpl,具体代码:

1
2
3
4
5
//frameworks/base/core/java/android/app/ContextImpl.java

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

如果 loadedApk 类型的mPackageinfo 不为 null,则调用其 getApplication 方法,否则调用 ActivityThread 的 getApplication 方法:

1
2
3
4
5
//frameworks/base/core/java/android/app/LoadedApk.java

Application getApplication() {
return mApplication ;
}

这个 mApplication 应该熟悉,是在前面提到的 LoadedApk 的 makeApplication 方法中注释 5 处被赋值的,是个Application 对象。就这样,我们获取到 Application Context。

Activity 的Context 创建过程

Activity的context创建过程时序图

应用程序进程的主线程管理类 ActivityThread 会调用内部类 ApplicationThread 的 scheduleLaunchActivity 方法来启动Activity,最终通过 H 类在主线程中处理启动事项,最终调用到 ActivityThread 的 performLaunchActivity 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//frameworks/base/core/java/android/app/ActivityThread.java

private Activity perfomLaunchActivity(ActivityClientRecord r , Intent customintent) {
...
ContextImpl appContext = createBaseContextForActivity(r);//l
Activity activity = null ;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl , component getClassName() , r.intent) ; //2
...
}catch(Exception e){
...
}

try {
...
if(activity != null) {
appContext.setOuterC nte t(activity) ; //3
//4
activity.attach(xxxx,xxxx);
...
}catch(Exception e){
...
}

在注释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 创建过程类似,这里先略过,后续有时间再记录。

谢谢你的鼓励