证明同一个应用只有一个 Application 对象实例
接上一篇文章 根 Activity 的启动后续 内的一个疑问点,就是 handleBindApplication 方法里会创建 Application 的对象实例,并执行 onCreate 方法,而 performLaunchActivity 方法里看似也会创建 Application 的对象实例,并执行 onCreate。显然不会重复执行,那么是怎么保证的,如何能证明呢?
先来证明 makeApplication 方法调用者是同一个对象。
performLaunchActivity 里的 loadedApk 对象来源于下面方法里的 ActivityClientRecord r 对象。通过查看其构造方法看到,变量 packageInfo 最终由 ActivityThread 对象调用 getPackageInfoNoCheck 方法生成。
//LaunchActivityItem
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,mProfilerInfo, client);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
//ActivityClientRecord
public ActivityClientRecord(...) {
//
this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo, compatInfo);
}
handleBindApplication 里的 loadedApk 对象也会由 ActivityThread 对象的 getPackageInfoNoCheck 方法生成。可见在这点上是相同的,那么他们会不会是同一个对象呢?getPackageInfoNoCheck 方法内部最终会调用下面这个方法。
//ActivityThread
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode, boolean registerPackage) {
//用来判断是否同一个进程,显然 aInfo 描述的进程就是应用进程
//而不管是 handleBindApplication 还是 performLaunchActivity 所在的进程也都是应用进程
//因此是 false
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized(mResourcesManager) {
WeakReference<LoadedApk> ref;
if (differentUser) {
ref = null;
} else if (includeCode) {
//includeCode 入参为 true,这里大概就是从 mPackages 里去找当前应用的 LoadedApk 对象
//在 handleBindApplication 的时候应该是找不到的,需要新建
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
//在 performLaunchActivity 的时候,应该是有值的,直接获取 handleBindApplication 时创建的
//LoadedApk 对象
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) {
packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0,
registerPackage);
if (differentUser) {
// Caching not supported across users
} else if (includeCode) {
//刚才创建好的 LoadedApk 对象就会存在 mPackages 里
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
}
}
return packageInfo;
}
显然这么分析下来同一个应用包应该有着相同的 packageInfo。有了相同的 packageInfo,在创建应用进程并初始化 Application 后,Application 对象就被保存在了 packageInfo 对象里的 mApplication 变量。然后在启动 Activity 时,虽然看去也是 make Application,但内部做了防重复创建判断,这就会直接返回 mApplication。
不过上面结论成立的前提是,两处调用的方法入参要一致。其实这点只要结合应用进程的启动过程,Activity 的启动过程,抓住我们需要关注的参数,也可以知道对于同一个应用来说,那些表示应用的信息肯定是一致的,不然这就和我们实际使用不符合了。