繁体   English   中英

ViewModel 如何在配置更改中幸存下来

[英]How ViewModel survives configuration change

我正在尝试在我的应用程序中使用 ViewModel。 我想到的问题是 View Model 如何在配置更改中幸存下来。 我读了很多博客文章说“

它将创建一个HolderFragment以添加到您的活动或片段中,它是不可见的,当配置更改时,活动被破坏,但持有者片段仍然存在

这是有道理的。但我试图对此进行更多探索,发现在支持库 27.1.0+中,他们已经删除了带有描述的 HolderFragment 说

弃用 ViewModelStores.of() 和它所依赖的 HolderFragment,因为它们不再需要android.googlesource 的链接

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

使用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的 hashmap 。

这个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);
        }
    }

基本上,为了在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对象返回一个包装器 object ( 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是 object ,它在配置更改后仍然存在,因此用于存储ViewModelStore 这就是为什么旋转后返回相同的ViewModelStore object - 它存储在与配置更改无关的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();
                }
            }
        }
    });
...   

类似的技巧用于存储FragmentViewModel

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM