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();
            }
        }
    }
});
相关推荐
TechMix2 小时前
【性能优化】RenderThread各工作阶段梳理
android·性能优化
草莓熊Lotso2 小时前
MySQL 内置函数指南:日期、字符串、数学函数实战
android·java·linux·运维·数据库·c++·mysql
2401_895521342 小时前
mysql中general_log日志详解
android·数据库·mysql
zh_xuan2 小时前
Android compose 自定义主题
android·compose
一只程序熊3 小时前
uniapp 高德地图 打开选择地址报错,也没有展示出附近的位置
android·uni-app
贤泽3 小时前
Android View 触摸事件分发机制
android·aosp
zh_xuan3 小时前
Android compose 使用viewModel
android·compose
0pen13 小时前
我用 AI 写了一个 Android 群控工具,从零到可用只花了一个下午
android·人工智能
雾江流3 小时前
LSPosed 2.0.0 | 强大的安卓Root框架,支持XP模块
android·软件工程