【问题标题】:How ViewModel survives configuration changeViewModel 如何在配置更改中幸存下来
【发布时间】:2023-04-04 12:32:01
【问题描述】:

我正在尝试在我的应用中使用 ViewModel。我想到的问题是视图模型如何在配置更改中幸存下来。我读过很多博客文章说“

它将创建一个 HolderFragment 以添加到您的活动或您的 片段,它是不可见的,当配置改变时,活动 已销毁,但持有者片段还活着

这是有道理的。但我尝试对此进行更多探索,发现在 support library 27.1.0+ 中,他们已删除了带有 Description 的 HolderFragment

弃用 ViewModelStores.of() 和它所依赖的 HolderFragment 因为它们不再需要了 link for android.googlesource

现在的问题是他们现在如何做同样的事情?

【问题讨论】:

    标签: android mvvm android-architecture-components android-viewmodel


    【解决方案1】:

    使用ViewModelProviders.of() 方法创建的ViewModel 存储在ViewModelStore hashmap 中,所以真正的问题是ViewModelStore 是如何存储的。

    对于活动,这个逻辑很简单。 ViewModelStore 使用onRetainNonConfigurationInstance 方法存储:

    @Override
        @Nullable
        public final Object onRetainNonConfigurationInstance() {
            Object custom = onRetainCustomNonConfigurationInstance();
    
            ViewModelStore viewModelStore = mViewModelStore;
            if (viewModelStore == null) {
                // No one called getViewModelStore(), so see if there was an existing
                // ViewModelStore from our last NonConfigurationInstance
                NonConfigurationInstances nc =
                        (NonConfigurationInstances) getLastNonConfigurationInstance();
                if (nc != null) {
                    viewModelStore = nc.viewModelStore;
                }
            }
    
            if (viewModelStore == null && custom == null) {
                return null;
            }
    
            NonConfigurationInstances nci = new NonConfigurationInstances();
            nci.custom = custom;
            nci.viewModelStore = viewModelStore;
            return nci;
        }
    

    对于片段,事情有点复杂。 FragmentManagerImpl 现在有一个名为 mNonConfig 的字段:

    private FragmentManagerViewModel mNonConfig;

    其中存储了 Fragment 的 UUID 和 ViewModelStore 的哈希图。

    这个mNonConfig字段在FragmentManagerImpl#attachController方法中初始化:

        public void attachController(@NonNull FragmentHostCallback host,
                @NonNull FragmentContainer container, @Nullable final Fragment parent) {
            if (mHost != null) throw new IllegalStateException("Already attached");
            mHost = host;
            mContainer = container;
            mParent = parent;
            if (mParent != null) {
                // Since the callback depends on us being the primary navigation fragment,
                // update our callback now that we have a parent so that we have the correct
                // state by default
                updateOnBackPressedCallbackEnabled();
            }
            // Set up the OnBackPressedCallback
            if (host instanceof OnBackPressedDispatcherOwner) {
                OnBackPressedDispatcherOwner dispatcherOwner = ((OnBackPressedDispatcherOwner) host);
                mOnBackPressedDispatcher = dispatcherOwner.getOnBackPressedDispatcher();
                LifecycleOwner owner = parent != null ? parent : dispatcherOwner;
                mOnBackPressedDispatcher.addCallback(owner, mOnBackPressedCallback);
            }
    
            // Get the FragmentManagerViewModel
            if (parent != null) {
                mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
            } else if (host instanceof ViewModelStoreOwner) {
                ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
                mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
            } else {
                mNonConfig = new FragmentManagerViewModel(false);
            }
        }
    

    【讨论】:

      【解决方案2】:

      基本上,为了在Activity 中检索ViewModel,应该调用ViewModelProviders.of(this).get(SomeViewModel.class)。现在,如果我们查看of,它如下所示:

      public static ViewModelProvider of(@NonNull FragmentActivity activity,
              @Nullable Factory factory) {
          Application application = checkApplication(activity);
          if (factory == null) {
              factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
          }
          return new ViewModelProvider(activity.getViewModelStore(), factory);
      }
      

      所以,重要的部分是这个方法 - activity.getViewModelStore(),因为它为您的所有 ViewModel 对象返回一个包装器对象(HashMap 持有者),如果它可以在配置更改中幸存下来,那么您的所有 ViewModel对象:

      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.");
          }
          if (mViewModelStore == null) {
              NonConfigurationInstances nc =
                      (NonConfigurationInstances) getLastNonConfigurationInstance();
              if (nc != null) {
                  // Restore the ViewModelStore from NonConfigurationInstances
                  mViewModelStore = nc.viewModelStore;
              }
              if (mViewModelStore == null) {
                  mViewModelStore = new ViewModelStore();
              }
          }
          return mViewModelStore;
      }
      

      mViewModelStore 将从 NonConfigurationInstances 恢复或从头开始创建。几乎,NonConfigurationInstances 是在配置更改后仍然存在的对象,因此用于存储ViewModelStore。这就是为什么在旋转后返回相同的 ViewModelStore 对象 - 它存储在独立于配置更改的 NonConfigurationInstances 中:

      如果您查看onRetainNonConfigurationInstance(),您实际上会发现您的ViewModelStore 保存在那里:

      public final Object onRetainNonConfigurationInstance() {
          ...
          NonConfigurationInstances nci = new NonConfigurationInstances();
          nci.custom = custom;
          nci.viewModelStore = viewModelStore;
          return nci;
      }
      

      此外,仅当由于非配置更改原因调用 onDestroy 时才会清除它:

      ...
          getLifecycle().addObserver(new LifecycleEventObserver() {
              @Override
              public void onStateChanged(@NonNull LifecycleOwner source,
                      @NonNull Lifecycle.Event event) {
                  if (event == Lifecycle.Event.ON_DESTROY) {
                      if (!isChangingConfigurations()) {
                          getViewModelStore().clear();
                      }
                  }
              }
          });
      ...   
      

      类似的技巧用于将ViewModel 存储为Fragment

      【讨论】:

      • 很好的答案,但onRetainNonConfigurationInstance 不是在 API 15 中被贬低了吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-07
      • 2019-04-20
      • 2016-09-08
      • 1970-01-01
      相关资源
      最近更新 更多