android 技术梳理

证明同一个应用只有一个 Application 对象实例

2021-01-28  本文已影响0人  jkwen

接上一篇文章 根 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 的启动过程,抓住我们需要关注的参数,也可以知道对于同一个应用来说,那些表示应用的信息肯定是一致的,不然这就和我们实际使用不符合了。

上一篇下一篇

猜你喜欢

热点阅读