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

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

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

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

1、旧Activity的销毁

1.1、AMS重建Activity时,会传入该Activitytoken。这样,在应用进程中就可以通过这个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变量进行判断。若为真,则调用ActivityretainNonConfigurationInstances()方法, 收集需要保存的状态信息, 将其何存至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、在新创建Activityattach()方法中,传入之前保存的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.NonConfigurationInstancesandroid.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之前,先将待销毁的旧ActivitymChangingConfigurations变量置为true。随后,依次执行旧ActivityonPause()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();
            }
        }
    }
});
相关推荐
恋猫de小郭4 小时前
Android 上为什么主题字体对 Flutter 不生效,对 Compose 生效?Flutter 中文字体问题修复
android·前端·flutter
三少爷的鞋4 小时前
不要让调用方承担你本该承担的复杂度 —— Android Data 层设计原则
android
李李李勃谦4 小时前
Flutter 框架跨平台鸿蒙开发 - 创意灵感收集
android·flutter·harmonyos
fengci.5 小时前
ctfshow其他(web396-web407)
android
JJay.5 小时前
Android 17 大屏适配变化解
android
TE-茶叶蛋6 小时前
结合登录页-PHP基础知识点解析
android·开发语言·php
alexhilton7 小时前
Jetpack Compose元球边缘效果
android·kotlin·android jetpack
y小花7 小时前
安卓音频子系统之USBAlsaManager
android·音视频
KevinCyao8 小时前
安卓android视频短信接口怎么集成?AndroidStudio视频短信开发指南
android