Android ViewModel为什么能够跨越Activity的生命周期?

ViewModel能够在系统配置发生变化时,跨越Activity的生命周期,其实质是在旧Activity即将销毁时,通过NonConfigurationInstances将ViewModel挂载到 ActivityClientRecord上

重建的新Activity与销毁的旧Activity是共用同一个ActivityClientRecord实例

在重建新Activity时,会从ActivityClientRecord中取出旧Activity销毁之前保存的NonConfigurationInstances

1、旧Activity的销毁

1.1、AMS重建Activity时,会传入该Activity的token。这样,在应用进程中就可以通过这个token获取到保存在mActivities中的ActivityClientRecord
复制代码
@Override
public void handleRelaunchActivity(@NonNull ActivityClientRecord tmp,
        @NonNull PendingTransactionActions pendingActions) {
    ......
    ActivityClientRecord r = mActivities.get(tmp.token);
    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r);
    if (r == null) {
        return;
    }

    r.activity.mConfigChangeFlags |= configChanges;
    r.mPreserveWindow = r.activity.mWindowAdded && tmp.mPreserveWindow;

    r.activity.mChangingConfigurations = true;

    handleRelaunchActivityInner(r, tmp.pendingResults, tmp.pendingIntents,
            pendingActions, tmp.startsNotResumed, tmp.overrideConfig, tmp.mActivityWindowInfo,
            "handleRelaunchActivity");
}
1.2、在重建新Activity之前,会先将失效的旧Activity销毁。同时,将ActivityClientRecord对应的字段置空,待新Activity创建完成后,再将该字段重新赋值为新Activity
复制代码
private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r,
        @Nullable List<ResultInfo> pendingResults,
        @Nullable List<ReferrerIntent> pendingIntents,
        @NonNull PendingTransactionActions pendingActions, boolean startsNotResumed,
        @NonNull Configuration overrideConfig, @NonNull ActivityWindowInfo activityWindowInfo,
        @NonNull String reason) {
    // Preserve last used intent, it may be set from Activity#setIntent().
    final Intent customIntent = r.activity.mIntent;
    // Need to ensure state is saved.
    if (!r.paused) {
        performPauseActivity(r, false, reason, null /* pendingActions */);
    }
    if (!r.stopped) {
        callActivityOnStop(r, true /* saveState */, reason);
    }

    handleDestroyActivity(r, false /* finishing */, true /* getNonConfigInstance */, reason);

    r.activity = null;
    r.window = null;
    r.hideForNow = false;
    // Merge any pending results and pending intents; don't just replace them
    if (pendingResults != null) {
        if (r.pendingResults == null) {
            r.pendingResults = pendingResults;
        } else {
            r.pendingResults.addAll(pendingResults);
        }
    }
    if (pendingIntents != null) {
        if (r.pendingIntents == null) {
            r.pendingIntents = pendingIntents;
        } else {
            r.pendingIntents.addAll(pendingIntents);
        }
    }
    r.startsNotResumed = startsNotResumed;
    r.overrideConfig = overrideConfig;
    r.mActivityWindowInfo.set(activityWindowInfo);

    handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent);
}
1.3、在销毁旧Activity之前,会对传入的getNonConfigInstance变量进行判断。若为真,则调用Activity的retainNonConfigurationInstances()方法, 收集需要保存的状态信息, 将其何存至Activity对应的ActivityClientRecord对象的android.app.Activity.NonConfigurationInstances类型的数据成员中
复制代码
/** Core implementation of activity destroy call. */
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    Class<? extends Activity> activityClass = null;
    if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
    activityClass = r.activity.getClass();
    r.activity.mConfigChangeFlags |= configChanges;
    if (finishing) {
        r.activity.mFinished = true;
    }

    performPauseActivityIfNeeded(r, "destroy");

    if (!r.stopped) {
        callActivityOnStop(r, false /* saveState */, "destroy");
    }
    if (getNonConfigInstance) {
        try {
            r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
        } catch (Exception e) {
            if (!mInstrumentation.onException(r.activity, e)) {
                throw new RuntimeException("Unable to retain activity "
                        + r.intent.getComponent().toShortString() + ": " + e.toString(), e);
            }
        }
    }
    ......
}

NonConfigurationInstances retainNonConfigurationInstances() {
    Object activity = onRetainNonConfigurationInstance();
    HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    // We're already stopped but we've been asked to retain.
    // Our fragments are taken care of but we need to mark the loaders for retention.
    // In order to do this correctly we need to restart the loaders first before
    // handing them off to the next activity.
    mFragments.doLoaderStart();
    mFragments.doLoaderStop(true);
    ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

    if (activity == null && children == null && fragments == null && loaders == null
            && mVoiceInteractor == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    nci.children = children;
    nci.fragments = fragments;
    nci.loaders = loaders;
    if (mVoiceInteractor != null) {
        mVoiceInteractor.retainInstance();
        nci.voiceInteractor = mVoiceInteractor;
    }
    return nci;
}

2、新Activity的创建

2.1、在新创建Activity的attach()方法中,传入之前保存的android.app.Activity.NonConfigurationInstances
复制代码
@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) {
    ......
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    ......
}
2.2、在新创建的Activity中获取ViewModelStore时,如果mViewModelStore数据成员为空,则会通过getLastNonConfigurationInstance()方法,从之前恢复的android.app.Activity.NonConfigurationInstances中,获取androidx.activity.ComponentActivity.NonConfigurationInstances
复制代码
@NonNull
@Override
public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    ensureViewModelStore();
    return mViewModelStore;
}

@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

/**
 * Retrieve the non-configuration instance data that was previously
 * returned by {@link #onRetainNonConfigurationInstance()}.  This will
 * be available from the initial {@link #onCreate} and
 * {@link #onStart} calls to the new instance, allowing you to extract
 * any useful dynamic state from the previous instance.
 *
 * <p>Note that the data you retrieve here should <em>only</em> be used
 * as an optimization for handling configuration changes.  You should always
 * be able to handle getting a null pointer back, and an activity must
 * still be able to restore itself to its previous state (through the
 * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
 * function returns null.
 *
 * <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
 * {@link Fragment#setRetainInstance(boolean)} instead; this is also
 * available on older platforms through the Android support libraries.
 *
 * @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
 */
@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

3、androidx.activity.ComponentActivity.NonConfigurationInstances与android.app.Activity.NonConfigurationInstances之间的转换

复制代码
static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

static final class NonConfigurationInstances {
    Object activity;
    HashMap<String, Object> children;
    FragmentManagerNonConfig fragments;
    ArrayMap<String, LoaderManager> loaders;
    VoiceInteractor voiceInteractor;
}
3.1、将androidx.activity.ComponentActivity.NonConfigurationInstance保存至android.app.Activity.NonConfigurationInstances类中的activity(位于对象内存模型的最开头)数据成员
复制代码
NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        // We're already stopped but we've been asked to retain.
        // Our fragments are taken care of but we need to mark the loaders for retention.
        // In order to do this correctly we need to restart the loaders first before
        // handing them off to the next activity.
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }
3.2、将android.app.Activity.NonConfigurationInstances类中的activity成员强转成androidx.activity.ComponentActivity.NonConfigurationInstance类型
复制代码
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

4、ViewModel的销毁

4.1、重建新Activity之前,先将待销毁的旧Activity的mChangingConfigurations变量置为true。随后,依次执行旧Activity的onPause()、onStop()、onDestroy()。
复制代码
@Override
public void handleRelaunchActivity(@NonNull ActivityClientRecord tmp,
        @NonNull PendingTransactionActions pendingActions) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    ......

    r.activity.mConfigChangeFlags |= configChanges;
    r.mPreserveWindow = r.activity.mWindowAdded && tmp.mPreserveWindow;

    r.activity.mChangingConfigurations = true;

    ......
}
4.2、只有当Activity不是在配置发生变化,而引起重建的情况下,ViewModel才会在Activity销毁时,进行clear操作。否则,不会跟随Activity销毁进行clear操作
复制代码
getLifecycle().addObserver(new LifecycleEventObserver() {
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            // Clear out the available context
            mContextAwareHelper.clearAvailableContext();
            // And clear the ViewModelStore
            if (!isChangingConfigurations()) {
                getViewModelStore().clear();
            }
        }
    }
});
相关推荐
TST-魚爷16 分钟前
易视讯会议摄像头电视安装教程
android·电脑·钉钉·腾讯会议·zoom会议
墨天梦10 小时前
B17_LifecycleLiveData与Flow
android·kotlin
_祝你今天愉快11 小时前
UI核心 - 绘制流程(测量、布局、绘制)
android
冬奇Lab12 小时前
LLM 驱动的自动化测试系列(07):移动端自动化(三)——Mobile-Agent-v3 与自研 GUI-Owl 模型路线
android·人工智能·测试
leobertlan15 小时前
痛苦系列 | DSP-02 频域切片:DTFT与DFT的探索
android·后端
leobertlan16 小时前
痛苦系列 | DSP-01 从连续到离散:DSP基础与采样
android·后端
事圆则缓19 小时前
Android 视频压缩上传实战:从 content Uri、转码到断点续传
android
冉冉同学1 天前
AI Agent 开始操作真实手机:移动端自动化的 3 个新考点
android·ai编程
帅得不敢出门1 天前
Android Framework关闭软件低电关机功能测试PMIC低电保护
android
恋猫de小郭1 天前
Flutter + EmbeddingGemma 2,谷歌发布完全端侧的 AI Edge Foresight
android·前端·flutter