Android ViewModel 如何确保它必须绑定相同的 Activity 或 Fragment(在屏幕旋转的情况下)?

How Android ViewModel insures that, it has to bind with the same Activity or Fragment(in case of Screen Rotation)?

我现在正在研究 Android ViewModel。一个问题一直在我脑海中浮现。尽管我们正在 onCreate 或 onCreateView 中创建新对象,ViewModel 如何确保在屏幕旋转后重新绑定相同的 Activity 或 Fragment?

有没有人对此有正确的答案,请告诉我。我试图在这么多教程中找到解决方案。

提前致谢!

创建 ViewModelProvider 的新实例时,第一个参数是 activity.getViewModelStore(),根据文档,

Returns the {@link ViewModelStore} associated with this activity

Overriding this method is no longer supported and this method will be made final in a future version of ComponentActivity. @return a {@code ViewModelStore} @throws IllegalStateException if called before the Activity is attached to the Application instance i.e., before onCreate()

它 returns 一个 ViewModelStore 对象。那么什么是 ViewModelStore

/**
 * Class to store {@code ViewModels}.
 * <p>
 * An instance of {@code ViewModelStore} must be retained through configuration changes:
 * if an owner of this {@code ViewModelStore} is destroyed and recreated due to configuration
 * changes, new instance of an owner should still have the same old instance of
 * {@code ViewModelStore}.
 * <p>
 * If an owner of this {@code ViewModelStore} is destroyed and is not going to be recreated,
 * then it should call {@link #clear()} on this {@code ViewModelStore}, so {@code ViewModels} would
 * be notified that they are no longer used.
 * <p>
 * Use {@link ViewModelStoreOwner#getViewModelStore()} to retrieve a {@code ViewModelStore} for
 * activities and fragments.
 */
 public class ViewModelStore {

 private final HashMap<String, ViewModel> mMap = new HashMap<>();

 final void put(String key, ViewModel viewModel) {
    ViewModel oldViewModel = mMap.put(key, viewModel);
    if (oldViewModel != null) {
        oldViewModel.onCleared();
    }
 }

 final ViewModel get(String key) {
    return mMap.get(key);
 }

 Set<String> keys() {
    return new HashSet<>(mMap.keySet());
 }

 /**
  *  Clears internal storage and notifies ViewModels that they are no longer used.
  */
 public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.clear();
    }
    mMap.clear();
  }
}

基本上,它是一个 class,它有一个 HashMap,其键是 DEFAULT_KEY + “:” + canonicalName,其中 DEFAULT_KEYandroidx.lifecycle.ViewModelProvider.DefaultKey,值是 ViewModel

这意味着每个 activity 和片段都有一个 ViewModelStore,它将所有声明的 ViewModels 分别保留在 activity 或片段中。

但是 ViewModelStore 如何在方向改变后幸存下来?

ViewModelStore的文档中定义

if an owner of this {@code ViewModelStore} is destroyed and recreated due to configuration * changes, new instance of an owner should still have the same old instance of * {@code ViewModelStore}.

因此活动有责任在方向更改期间保留 ViewModelStore

如果我们回到 activity.getViewModelStore() 实施,我们就会找到答案。

if (mViewModelStore == null) {
    NonConfigurationInstances nc =
        (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        // Restore the ViewModelStore from NonConfigurationInstances
        mViewModelStore = nc.viewModelStore;
    }
    if (mViewModelStore == null) {
        mViewModelStore = new ViewModelStore();
    }
}

activity 通过使用一种叫做 NonConfigurationInstances 的东西来做到这一点。 NonConfigurationInstances 是静态最终 class

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

所以它首先检查是否已经有一个 viewmodelstore 存储在 NonConfigurationInstances 如果没有它创建一个新的 ViewModelStore 或 returns 现有的 ViewModelStore .

那为什么 ViewModel 不能在低内存或 finish() 情况下生存?

ViewModelStore 有一个 clear() 方法:

/**
  *  Clears internal storage and notifies ViewModels that they are no longer used.
  */
public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.onCleared();
    }
    mMap.clear();
}

因此此 clear() 方法在 activity 的 onDestroy() 或清除 HashMap 的片段中被调用,除了在配置更改期间。这是代码:

if (mViewModelStore != null && !isChangingConfigurations) {
    mViewModelStore.clear();
}

现在您知道了 ViewModel 如何在配置更改后幸存下来的秘诀。

编码愉快…

重要提示我不相信将别人的工作归功于他人。为了使其在堆栈 overflow 上易于使用,我复制粘贴了文章 The curious case of Android ViewModel

中的所有内容