Activity框架04-Activity和Context的关系
Tip
基于 android-14.0.0_r45
背景¶
接触过 Android 的同学,对 Context 都不会陌生,有大量的场景都离不开 Context,比如:
- startActivity()
- getResources()
等等。
类图¶
plantuml(点击展开详情)
mermaid
classDiagram
direction BT
class Activity {
+ Activity()
- IBinder mToken
- Instrumentation mInstrumentation
~ ActivityThread mMainThread
- Application mApplication
~ Intent mIntent
+ getActivityToken() IBinder
+ getActivityThread() ActivityThread
~ attach(Context, ActivityThread, Instrumentation, IBinder) void
}
class Application {
+ Application()
+ onCreate() void
+ onTerminate() void
~ attach(Context) void
}
class Context {
+ Context()
+ startActivity(Intent) void
+ getMainLooper() Looper
+ startActivity(Intent, Bundle) void
+ getApplicationContext() Context
+ createApplicationContext(ApplicationInfo, int) Context
+ getActivityToken() IBinder
}
class ContextImpl {
- ContextImpl(ContextImpl, ActivityThread, LoadedApk, ContextParams, String, AttributionSource, String, IBinder, UserHandle, int, ClassLoader, String)
~ ActivityThread mMainThread
- Context mOuterContext
~ createAppContext(ActivityThread, LoadedApk) ContextImpl
+ startActivity(Intent) void
~ getImpl(Context) ContextImpl?
+ createApplicationContext(ApplicationInfo, int) Context
+ startActivity(Intent, Bundle) void
~ createAppContext(ActivityThread, LoadedApk, String) ContextImpl
}
class ContextThemeWrapper {
+ ContextThemeWrapper(Context, Theme)
+ ContextThemeWrapper()
+ ContextThemeWrapper(Context, int)
- Resources mResources
- int mThemeResource
- Theme mTheme
- initializeTheme() void
# onApplyThemeResource(Theme, int, boolean) void
+ getThemeResId() int
+ setTheme(int) void
+ setTheme(Theme) void
# attachBaseContext(Context) void
+ getTheme() Theme
}
class ContextWrapper {
+ ContextWrapper(Context)
~ Context mBase
# attachBaseContext(Context) void
+ getBaseContext() Context
+ startActivity(Intent, Bundle) void
+ startActivity(Intent) void
+ createApplicationContext(ApplicationInfo, int) Context
+ getActivityToken() IBinder
+ getApplicationContext() Context
+ getMainLooper() Looper
}
Activity --> ContextThemeWrapper
Application --> ContextWrapper
ContextImpl --> Context
ContextThemeWrapper --> ContextWrapper
ContextWrapper --> Context
Context 本身是一个抽象类,其子类有 ContextImpl 和 ContextWrapper ;间接子类有 Application 、ContextThemeWrapper、Activity。
Application Context¶
Activity Context 被传递到其他实例中,这可能导致自身被引用而发生泄漏。所以能使用 Application Context 则尽量使用 Application Context,除非是 Dialog 的 Context 这些场景必须是 Activity Context。
我们在谈及 OOM 的时候经常提到这个观点,这个观点是怎么来的我们先不深究。
但从这个观点我们可以推测出一个基本事实:Activity Context 和 Application Context 不是同一个 Context;从前面的类图也可以看出端倪。
ActivityThread.handleBindApplication()¶
在 Activity框架03-ActivityThread 中,我们提到 ActivityThread 的成员变量 mInstrumentation 初始化就是在 ActivityThread.handleBindApplication() 中进行的。
当时我们贴出来的代码只是 mInstrumentation 初始化相关的代码,其实 handleBindApplication() 方法还会初始化 Application 以及 Application Context。
public final class ActivityThread extends ClientTransactionHandler
implements ActivityThreadInternal {
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
...
// 创建 LoadedApk,并将其存储在 data.info 中
data.info = getPackageInfo(data.appInfo, mCompatibilityInfo, null /* baseLoader */,
false /* securityViolation */, true /* includeCode */,
false /* registerPackage */, isSdkSandbox);
...
// 创建 Instrumentation
if (ii != null) {
initInstrumentation(ii, data, appContext);
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
}
...
try {
// 创建 Application 、ContextImpl
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
// 将 Application 存储在 mInitialApplication 中
mInitialApplication = app;
try {
// 调用 Application.onCreate() 方法。
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
...
} finally {
...
}
...
}
}
- 创建 LoadedApk
- 创建 Instrumentation
- 创建 ContextImpl
- 创建 Application
- 调用 Application.onCreate()
创建 LoadedApk¶
public final class ActivityThread extends ClientTransactionHandler
implements ActivityThreadInternal {
// mPackages 的数据类型为 ArrayMap<String, WeakReference<LoadedApk>>
@GuardedBy("mResourcesManager")
@UnsupportedAppUsage
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
/**
* @param aInfo=data.appInfo
* @param compatInfo=mCompatibilityInfo
* @param baseLoader=null
* @param securityViolation=false
* @param includeCode=true
* @param registerPackage=false
* @param isSdkSandbox=false[一般都是false]
* @return
*/
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage, boolean isSdkSandbox) {
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (differentUser || isSdkSandbox) {
// 不支持跨用户和 SDK 沙箱的缓存
ref = null;
} else if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo != null) {
if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) {
if (packageInfo.getApplicationInfo().createTimestamp > aInfo.createTimestamp) {
// 缓存的 LoadedApk 比传入的 ApplicationInfo 更新,不更新缓存版本
Slog.w(TAG, "getPackageInfo() called with an older ApplicationInfo "
+ "than the cached version for package " + aInfo.packageName);
} else {
// 更新缓存的 ApplicationInfo Slog.v(TAG, "getPackageInfo() caused update to cached ApplicationInfo "
+ "for package " + aInfo.packageName);
List<String> oldPaths = new ArrayList<>();
LoadedApk.makePaths(this, aInfo, oldPaths);
packageInfo.updateApplicationInfo(aInfo, oldPaths);
}
}
return packageInfo;
}
// 加载新的 LoadedApk ...
packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode
&& (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
if (mSystemThread && "android".equals(aInfo.packageName)) {
packageInfo.installSystemApplicationInfo(aInfo,
getSystemContext().mPackageInfo.getClassLoader());
}
if (differentUser || isSdkSandbox) {
// 不支持跨用户和 SDK 沙箱的缓存
} else if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
return packageInfo;
}
}
- mPackages 的数据类型为
ArrayMap<String, WeakReference<LoadedApk>>
,记录着每一个包名所对应的LoadedApk
对象的弱引用。 - 当 mPackages 没有找到相应的 LoadedApk 对象,则创建该对象并加入到mPackages。
创建 ContextImpl¶
在 ActivityThread.handleBindApplication() 中,创建 LoadedApk 之后,就调用 LoadedApk.makeApplicationInner() 创建 Application,这其中也同时会创建 ContextImpl 。
public final class LoadedApk {
private Application mApplication;
public Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
return makeApplicationInner(forceDefaultAppClass, instrumentation,
/* allowDuplicateInstances= */ false);
}
/**
* 创建并初始化应用程序对象。
*
* @param forceDefaultAppClass 是否强制使用默认的应用程序类。
* @param instrumentation=null
* @param allowDuplicateInstances=false 是否允许创建重复的应用程序实例。
* @return 创建的应用程序对象。
*/
private Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation, boolean allowDuplicateInstances) {
if (mApplication != null) {
return mApplication;
}
...
synchronized (sApplications) {
final Application cached = sApplications.get(mPackageName);
if (cached != null) {
if (!"android".equals(mPackageName)) {
...
}
if (!allowDuplicateInstances) {
mApplication = cached;
return cached;
}
}
}
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) {
...
}
// 添加应用程序到所有应用程序列表中。
mActivityThread.mAllApplications.add(app);
// 将 Application 赋值给成员变量 mApplication
mApplication = app;
if (!allowDuplicateInstances) {
synchronized (sApplications) {
sApplications.put(mPackageName, app);
}
}
// 传进来的 instrumentation 为空
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
}
...
return app;
}
}
- 通过 ContextImpl.createAppContext() 创建一个 ContextImpl。
- 再通过 Instrumentation 反射出一个
android.app.Application
实例,并调用 Application.attach() 保存 Context。 - 创建 Application 后,会将 Application 赋值给 LoadedApk 的成员变量 mApplication 【这点很重要,后续会用到】
ContextImpl.createAppContext()¶
class ContextImpl extends Context {
/**
* 创建应用程序上下文(不指定操作包名)。
*
* @param mainThread 主线程,通常是 ActivityThread 实例。
* @param packageInfo 包信息,包含应用程序的详细信息。
* @return 创建的 ContextImpl 实例。
*/
@UnsupportedAppUsage
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
return createAppContext(mainThread, packageInfo, null);
}
/**
* 创建应用程序上下文(可以指定操作包名)。
*
* @param mainThread 主线程,通常是 ActivityThread 实例。
* @param packageInfo 包信息,包含应用程序的详细信息。
* @param opPackageName 操作包名,可以为 null。
* @return 创建的 ContextImpl 实例。
*/
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
String opPackageName) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
// 创建新的 ContextImpl 实例。
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
// 设置资源。
context.setResources(packageInfo.getResources());
// 根据上下文类型设置上下文类型变量。
context.mContextType = isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
: CONTEXT_TYPE_NON_UI;
return context;
}
}
如注释,这里其实很简单 就是调用 ContextImpl 的构造函数去创建一个 ContextImpl 。
ContextImpl 构造函数¶
class ContextImpl extends Context {
@UnsupportedAppUsage
private Context mOuterContext;
@UnsupportedAppUsage
final @NonNull ActivityThread mMainThread;
@UnsupportedAppUsage
final @NonNull LoadedApk mPackageInfo;
private ContextImpl(@Nullable android.app.ContextImpl container, @NonNull ActivityThread mainThread,
@NonNull LoadedApk packageInfo, @NonNull ContextParams params,
@Nullable String attributionTag, @Nullable AttributionSource nextAttributionSource,
@Nullable String splitName, @Nullable IBinder token, @Nullable UserHandle user,
int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
mOuterContext = this;
mMainThread = mainThread;
mPackageInfo = packageInfo;
}
}
创建 ContextImpl 的时候把 ActivityThread 和 LoadedApk 分别赋值给其成员变量 mMainThread 和 mPackageInfo。
创建 Application¶
创建 ContextImpl 之后,再回到 LoadedApk.makeApplicationInner() 中,接着创建 Application 。主要是通过 Instrumentation 反射出一个 android.app.Application
实例。
Instrumentation.newApplication()¶
public class Instrumentation {
/**
* 执行进程的 {@link Application} 对象的实例化。默认实现提供了正常的系统行为。
*
* @param cl 用于实例化对象的 ClassLoader。
* @param className 实现 Application 对象的类名。
* @param context 用于初始化应用程序的上下文。
*
* @return 新实例化的 Application 对象。
*/
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 获取应用程序组件工厂并实例化 Application 对象。
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
// 将 context 加到 Application 对象。
app.attach(context);
// 返回新实例化的 Application 对象。
return app;
}
}
通过 AppComponentFactory 以及 ClassLoader 去反射一个 Application 对象,并调用 Application.attach() 设置 Application 的 Context。
设置 Application Context¶
Application.attach()¶
public class Application extends ContextWrapper implements ComponentCallbacks2 {
/**
* @hide
*/
@UnsupportedAppUsage
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
}
Application 继承 ContextWrapper,所以我们继续看 ContextWrapper 类里的 attachBaseContext() 方法。
ContextWrapper.attachBaseContext()¶
public class ContextWrapper extends Context {
Context mBase;
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
}
到这一步很明显了,把 LoadedApk.makeApplicationInner() 创建 ContextImpl 保存到 Application 的父类成员变量 mBase 中。
总结¶
我再把 Context 、ContextImpl 、 ContextWrapper 、 Application 涉及到的都串起来:
public abstract class Context {
public abstract Context getApplicationContext();
}
class ContextImpl extends Context {
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
}
public class ContextWrapper extends Context {
Context mBase;
// LoadedApk.makeApplicationInner() 创建的 ContextImpl
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
@Override
public Context getApplicationContext() {
// 所以这里调到的其实是 ContextImpl.getApplicationContext()
return mBase.getApplicationContext();
}
}
public class Application extends ContextWrapper {
// 传 LoadedApk.makeApplicationInner() 创建 ContextImpl 进来
final void attach(Context context) {
attachBaseContext(context);
}
}
平时我们 getApplicationContext() 获取 Context,最终都是走到 ContextImpl.getApplicationContext()。
刚才我们在创建 ContextImpl 时,也就是在 LoadedApk.makeApplicationInner() 中,会将 Application 赋值给 LoadedApk 的成员变量 mApplication 。
ContextImpl 的成员变量 mPackageInfo 就是 LoadedApk,所以 mPackageInfo.getApplication() 其实也等同于 LoadedApk.getApplication(),也就是 LoadedApk 的成员变量 mApplication 。
我竟然绕了一大圈获取我自己,呵呵!
Activity Context¶
分析完 Application 的 Context 之后,我们继续分析 Activity Context。
ActivityThread.performLaunchActivity()¶
在 Activity框架03-ActivityThread 中,我们提到 Activity 是在 ActivityThread.performLaunchActivity() 中被创建的。而这一过程也同时会创建 Activity 的 Context 。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
...
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);
...
} catch (SuperNotCalledException e) {
...
} catch (Exception e) {
...
}
return activity;
}
- 通过调用 createBaseContextForActivity() 创建 ContextImpl
- 通过 Instrumentation 反射一个 Activity
- 调用 Activity.attach() 赋值给 Activity或者其父类
创建 ContextImpl¶
ActivityThread.createBaseContextForActivity()¶
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
// 获取要显示的显示ID
final int displayId = ActivityClient.getInstance().getDisplayId(r.token);
// 创建 ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
// 获取全局显示管理器
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// 用于调试目的,如果活动的包名包含“debug.use-second-display”系统属性的值作为子字符串,
// 并且存在一个二级显示屏,则在二级显示屏上显示其内容。
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
// 遍历所有的显示ID
for (int id : dm.getDisplayIds()) {
// 如果找到一个非默认显示的ID
if (id != DEFAULT_DISPLAY) {
// 获取与该显示ID兼容的显示
Display display = dm.getCompatibleDisplay(id, appContext.getResources());
// 创建针对该显示的上下文
appContext = (ContextImpl) appContext.createDisplayContext(display);
break;
}
}
}
return appContext;
}
这里很简单,我们接着看 ContextImpl.createActivityContext()。
ContextImpl.createActivityContext()¶
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
...
// 创建 ContextImpl 实例
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
attributionTag, null, activityInfo.splitName, activityToken, null, 0, classLoader,
null);
...
return context;
}
跟刚才的 Application Context 流程差不多,都是创建一个 ContextImpl 实例。我们不再分析 ContextImpl 构造函数。
创建 Activity¶
回到 ActivityThread.performLaunchActivity() 中,Activity 是通过 Instrumentation.newActivity() 被创建起来的。在 Activity框架03-ActivityThread 也有分析,这里就不再复述。
设置 Activity Context¶
还是继续回到 ActivityThread.performLaunchActivity() 中, Activity 创建完成之后就调用Activity.attach() 设置 Context。
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.attach()¶
public class Activity extends ContextThemeWrapper
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
if (newBase != null) {
newBase.setAutofillClient(getAutofillClient());
newBase.setContentCaptureOptions(getContentCaptureOptions());
}
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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, IBinder assistToken,
IBinder shareableActivityToken) {
attachBaseContext(context);
...
}
}
这里没有看到赋值的地方,还是得看父类 ContextThemeWrapper。
ContextThemeWrapper.attachBaseContext()¶
public class ContextThemeWrapper extends ContextWrapper {
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
}
继续看父类 ContextWrapper 。
ContextWrapper.attachBaseContext()¶
public class ContextWrapper extends Context {
Context mBase;
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
}
这里跟前面的分析就是一样的了,还是把 ActivityThread.createBaseContextForActivity() 创建的 ContextImpl 赋值给了 mBase。
总结¶
我再把 Context 、ContextImpl 、 ContextWrapper 、 Activity 涉及到的都串起来:
public abstract class Context {
}
class ContextImpl extends Context {
}
public class ContextWrapper extends Context {
Context mBase;
// ActivityThread.createBaseContextForActivity() 创建的 ContextImpl
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
public Context getBaseContext() {
return mBase;
}
}
public class Activity extends ContextThemeWrapper
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
if (newBase != null) {
newBase.setAutofillClient(getAutofillClient());
newBase.setContentCaptureOptions(getContentCaptureOptions());
}
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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, IBinder assistToken,
IBinder shareableActivityToken) {
attachBaseContext(context);
...
}
}
有部分同学喜欢在 Activity 中通过 getBaseContext() 获取到 Context,这里获取到的 Context 就是 ActivityThread.createBaseContextForActivity() 创建的 ContextImpl 。
除了 Activity 跟 Context 有着千丝万缕的关系之外,Android四大组件的另外三个也跟 Context 有着不开分割的关系。 我们后续讲到别的组件时再继续分析。