Android Context 之到底有几个 Context 对象?

Android 应用中到底有多少个 Context 对象?

到底有几个 Context?

一种说法是 Android 应用中 Context 对象(准确地说是 ContextImpl 对象)的个数为 1 + n + m

其中:

  • 1 表示 Application
  • n 表示 存活的 Activity 的个数
  • m 表示 存活的 Service 的个数

简单来说:应用中有多少个 Activity 或者 Service,就会有多少 Context 对象(外加1个 Application)。

我在 Cursor 中问 AI 同样的问题,AI 明确地告诉我 ContextImpl 对象上的数量是 1 + n + m 个。

从 Context 接口相关的类图来看,

Application, Activity 和 Service 都是 ContextWrapper 的子类。每个 ContextWrapper 持有一个 ContextImpl 对象,所以直观上来讲共 1 + n + m 个对象这个结论似乎是对的。

但是 Framework 代码中很多地方在创建 ContextImpl 对象,数量明显多于 1 + n + m。我有些好奇,所以在 ContextImpl 代码中补充一些逻辑和日志用来追踪对象数量以验证上面这个结论。

跟踪 ContextImpl 数量

改动点:在 ContextImpl.java 中添加 sInstanceCount 字段来记录对象数量,并且在各个创建实例的静态方法中打印出当前对象的 mContextType

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
34
35
36
37
+    private static int sInstanceCount = 0;
+ private static final Object sInstanceCountLock = new Object();

+ synchronized (sInstanceCountLock) {
+ sInstanceCount++;
+ Log.e("cmcmcm", Integer.toString(sInstanceCount), new Exception());
+ }

mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;

+ synchronized (sInstanceCountLock) {
+ sInstanceCount--;
+ }
super.finalize();
}

+ public static int getInstanceCount() {
+ synchronized (sInstanceCountLock) {
+ return sInstanceCount;
+ }
+ }
+
+ private static String ctxType(int t) {
+ if (t == CONTEXT_TYPE_NON_UI) {
+ return "CONTEXT_TYPE_NON_UI";
+ } else if (t == CONTEXT_TYPE_DISPLAY_CONTEXT) {
+ return "CONTEXT_TYPE_DISPLAY_CONTEXT";
+ } else if (t == CONTEXT_TYPE_ACTIVITY) {
+ return "CONTEXT_TYPE_ACTIVITY";
+ } else if (t == CONTEXT_TYPE_WINDOW_CONTEXT) {
+ return "CONTEXT_TYPE_WINDOW_CONTEXT";
+ } else if (t == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI) {
+ return "CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI";
+ }
+ return "CONTEXT_TYPE_NON_UI";
+ }
+

然后在 App 中通过反射调用 ContextImpl.getInstanceCount() 方法,获取当前的实例数数量并输出相应日志。

以下是一个只包含单个 Activity 的 Android App 启动时的日志:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
E  1 CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createSystemContext(ContextImpl.java:3323)
E at android.app.ActivityThread.getSystemContext(ActivityThread.java:2851)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6864)
E at ...

E 2 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3360)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3354)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6879)
E at ...

E 3 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createDeviceProtectedStorageContext(ContextImpl.java:2993)
E at android.app.ActivityThread.setupGraphicsSupport(ActivityThread.java:6641)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6902)
E at ...

E 4 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3360)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3354)
E at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1454)
E at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1395)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6959)
E at ...

E 5 CONTEXT_TYPE_ACTIVITY
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createActivityContext(ContextImpl.java:3398)
E at android.app.ActivityThread.createBaseContextForActivity(ActivityThread.java:3867)
E at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3672)
E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922)
E at ...

E 6 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createConfigurationContext(ContextImpl.java:2759)
E at android.content.ContextWrapper.createConfigurationContext(ContextWrapper.java:1158)
E at com.android.internal.policy.DecorContext.<init>(DecorContext.java:58)
E at com.android.internal.policy.PhoneWindow.generateDecor(PhoneWindow.java:2396)
E at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2762)
E at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:465)
E at android.app.Activity.setContentView(Activity.java:3679)
E at com.example.helloaosp.MainActivity.onCreate(MainActivity.java:24)
E at android.app.Activity.performCreate(Activity.java:8595)
E at android.app.Activity.performCreate(Activity.java:8573)
E at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1456)
E at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3764)
E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922)
E at ...

日志格式如下:

1
2
E <当前 ContextImpl 的总数> <当前 ContextImpl 对象的Type>
E <创建 ContextImpl 对象的调用栈>

以上日志显示应用启动加一个 Activity 的启动就创建 6 个 ContextImpl 对象。

Context 类型

这里简单了解一下 Context 的类型。

1
2
3
4
5
6
7
@IntDef(prefix = "CONTEXT_TYPE_", value = {
CONTEXT_TYPE_NON_UI,
CONTEXT_TYPE_DISPLAY_CONTEXT,
CONTEXT_TYPE_ACTIVITY,
CONTEXT_TYPE_WINDOW_CONTEXT,
CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** @hide */
@Override
public boolean isUiContext() {
switch (mContextType) {
case CONTEXT_TYPE_ACTIVITY:
case CONTEXT_TYPE_WINDOW_CONTEXT:
case CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI:
return true;
case CONTEXT_TYPE_DISPLAY_CONTEXT:
case CONTEXT_TYPE_NON_UI: {
return false;
}
default:
return false;
}
}

/** @hide */
@Override
public boolean isConfigurationContext() {
return isUiContext() || mIsConfigurationBasedContext;
}

以上代码片断均来自 Android 14 ContextImpl.java。从代码我们得知,Context 目前分5种不同的类型,这些类型可以分成两大类:

  • 一类是 UI 相关的 Context,包括
    • CONTEXT_TYPE_ACTIVITY
    • CONTEXT_TYPE_WINDOW_CONTEXT
    • CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
  • 另一类是非 UI 相关的 Context,包括
    • CONTEXT_TYPE_DISPLAY_CONTEXT
    • CONTEXT_TYPE_NON_UI

还有一种没有明确定义的 Context 类别是 Configuration 相关的 Context,UI 相关的 Context 以及 mIsConfigurationBasedContexttrue 的 Context 都认为是这一类的。

不同的场景会创建对应类型的 ContextImpl 对象。我们可以猜到,创建新的 Activity 对象会导致创建一个类型为 CONTEXT_TYPE_ACTIVITY 的 ContextImpl 对象。

ContextImpl 对象的创建

Application 相关

启动 App 时会创建 Application 对象,这个过程中会导致4个 ContextImpl 对象的创建。

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
E  1 CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createSystemContext(ContextImpl.java:3323)
E at android.app.ActivityThread.getSystemContext(ActivityThread.java:2851)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6864)
E at ...

E 2 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3360)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3354)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6879)
E at ...

E 3 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createDeviceProtectedStorageContext(ContextImpl.java:2993)
E at android.app.ActivityThread.setupGraphicsSupport(ActivityThread.java:6641)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6902)
E at ...

E 4 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3360)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3354)
E at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1454)
E at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1395)
E at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6959)
E at ...

ActivityThread.handleBindApplication() 方法的调用导致4个 ContextImpl 对象被创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

// Line 6864
HardwareRenderer.setContextForInit(getSystemContext());

// Line 6879

final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);

// Line 6902
setupGraphicsSupport(appContext);

// Line 6959

app = data.info.makeApplicationInner(data.restrictedBackupMode, null);

但是通过分析代码发现,只有 Line 6864 和 Line 6959 创建的 ContextImpl 对象被持有并在 App 的整个生命周期中存活下来。

Line 6864 对应于 ActivityThread.getSystemContext() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final class ActivityThread extends ClientTransactionHandler {

private ContextImpl mSystemContext;

@Override
@UnsupportedAppUsage
public ContextImpl getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
mSystemContext = ContextImpl.createSystemContext(this);
}
return mSystemContext;
}
}
}

这里第10行创建的 mSystemContextActivityThread 一直持有。

Line 6959 对应于 Application 对象的创建以及其 baseContext 的关联。

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
34
35
36
37
38
39
40
41
42
/**
* Local state maintained about a currently loaded .apk.
* @hide
*/
public final class LoadedApk {
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
return makeApplicationInner(forceDefaultAppClass, instrumentation,
/* allowDuplicateInstances= */ true);
}

private Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation, boolean allowDuplicateInstances) {
Application app = null;

final String myProcessName = Process.myProcessName();
String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
myProcessName);
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
}
}
}

public class Instrumentation {
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
}
  • app 是 appContext 的 mOuterContext
  • appContext 是 app 的 mBase

这里第24行创建的 appContextApplication 一直持有。

小结:启动 App 时会创建 Application 对象,这个过程中会导致4个 ContextImpl 对象的创建。但真正存活的 ContextImpl 对象只有2个。

Activity 相关

启动 Activity 时会创建 Activity 对象,这个过程中会导致2个 ContextImpl 对象的创建。

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
E  5 CONTEXT_TYPE_ACTIVITY
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createActivityContext(ContextImpl.java:3398)
E at android.app.ActivityThread.createBaseContextForActivity(ActivityThread.java:3867)
E at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3672)
E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922)
E at ...

E 6 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createConfigurationContext(ContextImpl.java:2759)
E at android.content.ContextWrapper.createConfigurationContext(ContextWrapper.java:1158)
E at com.android.internal.policy.DecorContext.<init>(DecorContext.java:58)
E at com.android.internal.policy.PhoneWindow.generateDecor(PhoneWindow.java:2396)
E at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2762)
E at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:465)
E at android.app.Activity.setContentView(Activity.java:3679)
E at com.example.helloaosp.MainActivity.onCreate(MainActivity.java:24)
E at android.app.Activity.performCreate(Activity.java:8595)
E at android.app.Activity.performCreate(Activity.java:8573)
E at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1456)
E at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3764)
E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922)
E at ...

ActivityThread.handleLaunchActivity() 方法的调用导致两个 ContextImpl 对象被创建。

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
public final class ActivityThread extends ClientTransactionHandler {

/**
* Extended implementation of activity launch. Used when server requests a launch or relaunch.
*/
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, int deviceId, Intent customIntent) {
final Activity a = performLaunchActivity(r, customIntent);

return a;
}


/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
ContextImpl appContext = createBaseContextForActivity(r);

appContext.setOuterContext(activity);
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.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
}
}
  • activity 是 appContext 的 mOuterContext
  • appContext 是 activity 的 mBase

这里第21行创建的 appContext 被 Activity 一直持有。

启动 Activity 过程会调用 PhoneWindow.generateDecor() 创建 DecorView,导致创建 DecorContext 对象。而 DecorContext 的 baseContext 是一个新创建的 ContextImpl 对象 ,(见第41行 baseContext.createConfigurationContext())

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class PhoneWindow extends Window implements MenuBuilder.Callback {
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, this);
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
}

public class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private Resources mResources;
private ContentCaptureManager mContentCaptureManager;

private WeakReference<Context> mContext;

@VisibleForTesting
public DecorContext(Context baseContext, PhoneWindow phoneWindow) {
super(null /* base */, null);
setPhoneWindow(phoneWindow);
// TODO(b/149790106): Non-activity context can be passed.
final Display display = phoneWindow.getContext().getDisplayNoVerify();
final Context displayContext;
if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
// TODO(b/166174272): Creating a display context for the default display will result
// in additional resource creation.
displayContext = baseContext.createConfigurationContext(Configuration.EMPTY);
displayContext.updateDisplay(Display.DEFAULT_DISPLAY);
} else {
displayContext = baseContext.createDisplayContext(display);
}
attachBaseContext(displayContext);
}

void setPhoneWindow(PhoneWindow phoneWindow) {
mPhoneWindow = phoneWindow;
final Context context = phoneWindow.getContext();
mContext = new WeakReference<>(context);
mResources = context.getResources();
}
}

(以上这段代码好像有 bug,DecorContext.mContext 定义成弱引用并没有意义。之所以定义成弱引用,很可能是为了避免强引用 Activity。但是 DecorContext.mPhoneWindow 会强引用 PhoneWindow 对象,所以最终还是会强引用 Activity。那么 DecorContext.mContext 定义成弱引用的意义是?)

这里第41行创建的 displayContext 最终也会被 Activity 一直持有。引用链为:

1
2
3
4
Activity.mWindow -> PhoneWindow
PhoneWindow.mDecor -> DecorView
DecorView.mBase -> DecorContext
DecorContext.displayContext -> ContextImpl

小结:启动 App 时会创建 Activity 对象,这个过程中会导致2个 ContextImpl 对象的创建。第一个对象将作为 Activity.mBase,第二个对象也间接被 Activity技术,这2个 ContextImpl 对象一直在 Activity 生命周期期间存活。

Service 相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
E 7 CONTEXT_TYPE_NON_UI
E java.lang.Exception
E at android.app.ContextImpl.<init>(ContextImpl.java:3494)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3360)
E at android.app.ContextImpl.createAppContext(ContextImpl.java:3354)
E at android.app.Service.createServiceBaseContext(Service.java:1004)
E at android.app.ActivityThread.handleCreateService(ActivityThread.java:4628)
E at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown Source:0)
E at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2264)
E at android.os.Handler.dispatchMessage(Handler.java:106)
E at android.os.Looper.loopOnce(Looper.java:205)
E at android.os.Looper.loop(Looper.java:294)
E at android.app.ActivityThread.main(ActivityThread.java:8177)
E at java.lang.reflect.Method.invoke(Native Method)
E at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:972)
E
I MyService.onCreate
I MyService.onStartCommand
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class ActivityThread extends ClientTransactionHandler
private void handleCreateService(CreateServiceData data) {
ContextImpl context = ContextImpl.getImpl(service
.createServiceBaseContext(this, packageInfo));
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
}
}
public abstract class Service extends ContextWrapper implements ComponentCallbacks2,
public Context createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo) {
return ContextImpl.createAppContext(mainThread, packageInfo);
}
}

小结:启动 App 时会创建 Service 对象,这个过程中会导致1个 ContextImpl 对象的创建,这个对象将作 Service.mBase 一直在 Service 生命周期期间存活。

结论

假设 n 表示 存活的 Activity 的个数,m 表示 存活的 Service 的个数,

  • 从 ContextImpl 对象的在系统中的创建次数考虑,Android 应用中 Context 对象的个数为:4 + 2 * n + m
  • 从 ContextImpl 对象的在系统中的存活个数考虑,Android 应用中 Context 对象的个数为:2 + 2 * n + m

我们之所以普遍认为 Android 应用中 Context 对象的个数为 1 + n + m,一个可能的原因是视角不同:从系统角度出发,能感知到更多的 Context 对象 (比如 ActivityThread.mSystemContext 对应的 Context);而从用户/App 角度发出,能感知到的 Context 对象基本就等同于继承自 Context 接口的组件对象本身了。