ViewModel 如何在配置更改后存活下来
How ViewModel survives configuration change
我正在尝试在我的应用程序中使用 ViewModel。我想到的问题是视图模型如何在配置更改后幸存下来。我读了很多博客文章说“
It will create a HolderFragment to add to your activity or your
fragment, it's invisible, when the configuration changed, activity
destroyed, but holder fragment is still alive
这使得 sense.But 我试图对此进行更多探索,发现在 支持库 27.1.0+ 中,他们删除了 HolderFragment,其描述为
Deprecate ViewModelStores.of() and the HolderFragment it relies on
as they are no longer needed link for android.googlesource.
现在的问题是他们现在是如何做同样的事情的?
用 ViewModelProviders.of()
方法创建的 ViewModels 存储在 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;
其中存储了片段的 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);
}
}
基本上,要在 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()
因为它 returns 是所有 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();
}
}
}
});
...
类似的技巧用于为 Fragment
存储 ViewModel
。
我正在尝试在我的应用程序中使用 ViewModel。我想到的问题是视图模型如何在配置更改后幸存下来。我读了很多博客文章说“
It will create a HolderFragment to add to your activity or your fragment, it's invisible, when the configuration changed, activity destroyed, but holder fragment is still alive
这使得 sense.But 我试图对此进行更多探索,发现在 支持库 27.1.0+ 中,他们删除了 HolderFragment,其描述为
Deprecate ViewModelStores.of() and the HolderFragment it relies on as they are no longer needed link for android.googlesource.
现在的问题是他们现在是如何做同样的事情的?
用 ViewModelProviders.of()
方法创建的 ViewModels 存储在 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;
其中存储了片段的 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);
}
}
基本上,要在 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()
因为它 returns 是所有 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();
}
}
}
});
...
类似的技巧用于为 Fragment
存储 ViewModel
。