从 ViewModelProvider(this).get(xxViewModel.class) 讲起
首先,我们看看 ViewModelProvider
的构造函数:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
ViewModelStore
ViewModelProvider
的构造函数接收一个 ViewModelStoreOwner
,而 ViewModelStoreOwner
接口只有一个 getViewModelStore
方法,它返回一个 ViewModelStore
。所以我们看看 ViewModelStore
:
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();
}
}
实现非常简单:实现中只有一个用于存储 ViewModel
的 hash 表:
-
key 是由
ViewModelProvider
生成的:DEFAULT_KEY + ":" + canonicalName
,其中DEFAULT_KEY = androidx.lifecycle.ViewModelProvider.DefaultKey
。 -
value 是
ViewModel
的实例。
ViewModelStore
的 owner 类,必须保证在发生配置改变时,发生重建后,依然返回同一个 ViewModelStore
实例。我们的 ComponentActivity
就是一个 ViewModelStoreOwner
,我们在后面小节将会看看在重建后,它是如何返回同一个 ViewModelStore
实例的。
ViewModelProvider.Factory
ViewModelProvider
的带两个参数的构造函数的第二个参数是一个 Factory
:
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
从注释和接口代码中我们可以看出,它是一个实例化 ViewModel
的工厂,只有一个产生 ViewModel
实例的 create
方法。从方法签名中我们就可以看出,它接收 Class
,通过反射之类的方法来构造 ViewModel
。通常我们默认使用的是 ViewModelProvider.NewInstanceFactory
:
/**
* Simple factory, which calls empty constructor on the give class.
*/
public static class NewInstanceFactory implements Factory {
private static NewInstanceFactory sInstance;
/**
* Retrieve a singleton instance of NewInstanceFactory.
*
* @return A valid {@link NewInstanceFactory}
*/
@NonNull
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
可以看到,默认的 NewInstanceFactory
只是简单地反射调用 ViewModel
的无参构造函数来实例化传入的 ViewModel
。
Activity 中 ViewModel 的创建和获取
我们从常用的获取 ViewModel
方法出发:
ChronometerViewModel chronometerViewModel
= new ViewModelProvider(this).get(ChronometerViewModel.class);
👇
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
// 这里的 get 方法就生成了我们上面 ViewModelStore 中的 key 值了
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
👇
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 从 ViewModelStore 中获取 ViewModel 实例
ViewModel viewModel = mViewModelStore.get(key);
// 如果获取到,且确实是我们想要获取的 ViewModel 类型,则直接返回
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
// 如果没有获取到 ViewModel 实例,则调用 Factory 进行创建
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
// 将创建出来的 ViewModel 存入 ViewModelStore
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
Activity 中 ViewModel 的保存和恢复
ViewModel
的保存和恢复是利用了 Activity.onRetainNonConfigurationInstance
机制。我们来看支持 ViewModel
的 ComponentActivity.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;
}
// 将 ViewModelStore 保存在 NonConfigurationInstances 中。保存了 ViewModelStore 也就保存了 ViewModel
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
从上面我们可以看到,ViewModel
的保存是依赖于 onRetainNonConfigurationInstance
的。ViewModel
的恢复我们来看看 ViewModelProvider
的构造方法:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
上面传入 Activity
作为 ViewModelStoreOwner
,当调用它的 getViewModelStore
时 ViewModelStore
也就被恢复了:
@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.");
}
if (mViewModelStore == null) {
// 这里获得的 NonConfigurationInstance 就是之前通过 onRetainNonConfigurationInstance 保存的
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
我们通过方法 getLastNonConfigurationInstance
获取到之前保存的 NonConfigurationInstances
,从而取出其中的 ViewModelStore
。
通过上面的分析,我们知道,保存恢复 ViewModel
的关键在于 onRetainNonConfigurationInstance
和 getLastNonConfigurationInstance
机制是如何起作用的?
通过查找方法引用,我们可以看到 onRetainNonConfigurationInstance
方法其实是在 Activity.retainNonConfigurationInstances
方法中被调用的:
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;
}
// 注意 Activity 中的 NonConfigurationInstances 类与 ComponentActivity 中的 NonConfigurationInstances 类不一样
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity; // 这里将 onRetainNonConfigurationInstance 返回值保存了下来,其中就包含了 ViewModelStore
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
而 Activity.retainNonConfigurationInstances
方法是在 ActivityThread.performDestroyActivity
方法中被调用的。而 ComponentActivity
中的 mLastNonConfigurationInstances
则是在 ComponentActivity.attach
方法中,由 ActivityThread.performLaunchActivity
时主动调用传入进来的。
因此综上所述,ViewModelStore
在 onRetainNonConfigurationInstance
时被保存在了 NonConfigurationInstances
实例中,该实例被 ActivityThread
持有。下次 Activity
重建时,由 ActivityThread.performLaunchActivity
方法中调用 Activity.attach
方法,再讲 NonConfigurationInstances
实例传给重建后的 Activity
。
Activity 中 ViewModel 的销毁
通过上节我们知道 ViewModel
可以跨越 Activity
的生命周期和重建,那么 Activity
的 ViewModel
怎么知道需要在什么时候销毁呢?
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
正是借助了生命周期的回调,在 ON_DESTROY
回调时,通过 isChangingConfigurations
方法判断该 Activity
是否正处于因配置改变而重建中,如果不是的话,则可以清楚 ViewModel
。
Fragment 中 ViewModel 的创建和获取
由于 Fragment
同样也是实现了 ViewModelStoreOwner
接口,因此同在 Activity
中创建和获取 ViewModel
一样,给 ViewModelProvider
的构造函数传入自己的实例即可:
mSeekBarViewModel = new ViewModelProvider(this).get(SeekBarViewModel.class);
如果 Fragment
要和其宿主 Activity
共用一个 ViewModel
,则可以向 ViewModelProvider
的构造函数传入其宿主 Activity
即可:
mSeekBarViewModel = new ViewModelProvider(requireActivity()).get(SeekBarViewModel.class);
但是与 Activity
中创建 ViewModel
不同,因为创建和获取 ViewModel
在于 getViewModelStore
方法,而这个方法的实现上,Activity
和 Fragment
是不相同的。
-
Fragment.getViewModelStore
是从FragmentManager.getViewModelStore
中获取的。 -
FragmentActivity.getViewModelStore
是从ComponentActivity.getViewModelStore
获取的,它是直接存储在mViewModelStore
中的。
因此我们下面分析的重点应该在于 FragmentManager.getViewModelStore
的机制。
FragmentActivity 在初始化时做了什么
我们可以注意到在 FragmentActivity.onCreate
中调用了 mFragments.attachHost
:
mFragments.attachHost(null /*parent*/);
最终调用到了 FragmentManager.attachController
(注意,这里的 FragmentManager
就是根 Fragment
中的 mFragmentManager
):
// 该方法在 FragmentManager 中。host 为 FragmentActivity.HostCallbacks。
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
...
// Get the FragmentManagerViewModel
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) { // 👈调用到这里
// 这里最终获取的是 FragmentActivity 的 ViewModelStore
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
// mNonConfig 这个 FragmentManager 对应的 Fragment 对应的 ViewModel。
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
因为在 FragmentActivity
中传入的 parent
为 null
,且 FragmentActivity.HostCallbacks
实现了 ViewModelStoreOwner
,因此会走到上面的第二个条件分支。而 FragmentActivity.HostCallbacks.getViewModelStore
就是调用的 FragmentActivity.getViewModelSotre
,因此上面代码创建的 FragmentManagerViewModel
的实例 mNonConfig
,其实存入到了 FragmentActivity
的 ViewModelStore
中,而且还换存在了 FragmentManager.mNonConfig
(注意 mNonConfig
是一个 FragmentManagerViewModel
的实例) 中,这两点很重要。
在 Fragment 中获取 ViewModel
我们知道关键点在于调用的 Fragment.getViewModelStore
:
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
// 这里的 mFragmentManager 实例就是 FragmentActivity.HostCallbacks 内部类中的 mFragmentManager,
// 也就是在上一节中我们提到拥有 FragmentManagerViewModel 的那个 FragmentManager
return mFragmentManager.getViewModelStore(this);
}
// FragmentManager 👇
ViewModelStore getViewModelStore(@NonNull Fragment f) {
// 这里的 mNonConfig 根据上节,是一个 FragmentManagerViewModel 的实例
return mNonConfig.getViewModelStore(f);
}
从上面代码和注释中我们可以知道
- 根
Fragment
的getViewModelStore
最终是从FragmentManagerViewModel.getViewModelStore
中获取的。这与FragmentActivity: ComponentActivity
的getViewModelStore
来源不一样,因此在FragmentActivity
和Fragment
中都获取同样类型(类名一样)的ViewModel
,获取出来的实例却是不一样的。 - 根据上一节中我们可以知道,根
Fragment.getViewModelStore
的最终调用处FragmentManagerViewModel.getViewModelStore
中的FragmentManagerViewModel
实例,其实在FragmentActivity.onCreate
时就已经存入了FragmentActivity
的ViewModelStore
中。也就是说它们之间的结构是下面这样的:
FragmentManagerViewModel 与 Activity 的关系
Fragment 创建时做了什么
正如 FragmentActivity
在 onCreate
时就已经把,用于存储 根Fragment
对应的 ViewModelStore
的 FragmentManagerViewModel
,缓存在了 FragmentActivity
自己的 ViewModelStore
中,那么 根Fragment
是否也对 子Fragment
做了同样的准备?
Fragment
中有一个成员变量 mChildFragmentManager
,它是管理 子Fragment
的 FragmentManager
。因此我们可以注意到,根 Fragment
对应的 FragmentManager
是 FragmentActivity.FragmentController.mHost.mFragmentManager
,而 子Fragment
对应的 FragmentManager
是 Fragment.mChildFragmentManager
。
Fragment
中还有一个成员变量 mFragmentManager
,它就是管理 本Fragment
的 FragmentManager
,它的实例其实是来自 父Fragment.getChildFragmentManager
。
当 根Fragment
关联到 Activity
上时,会调用其 performAttach
方法:
void performAttach() {
// 类比于 FragmentActivity.onCreate 中的 mFragments.attachHost -> mHost.mFragmentManager.attachController
mChildFragmentManager.attachController(mHost, new FragmentContainer() {
@Override
@Nullable
public View onFindViewById(int id) {
if (mView == null) {
throw new IllegalStateException("Fragment " + this + " does not have a view");
}
return mView.findViewById(id);
}
@Override
public boolean onHasView() {
return (mView != null);
}
}, this);
mState = ATTACHED;
mCalled = false;
onAttach(mHost.getContext());
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onAttach()");
}
}
正如 FragmentActivity
中最终调用到了 HostCallbacks.mFragmentManager.attachController
,Fragment.performAttach
最终也调用到了 mChildFragmentManager.attachController
:
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
...
// Get the FragmentManagerViewModel
if (parent != null) {
此时 parent != null,走这里 👇。下面的 parent 是指的根 Fragment,所以下面的 mFragmentManager 是 FragmentActivity 的 FragmentManager
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
最终调用到了 FragmentActivity
的 FragmentManagerViewModel.getChildNonConfig(parent)
:
// 参数 f 是 根Fragment
FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
if (childNonConfig == null) {
childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
mChildNonConfigs.put(f.mWho, childNonConfig);
}
return childNonConfig;
}
如上所示,FragmentManagerViewModel
中还有一个数据结构 mChildNonConfigs
,它是一个 key 为 FragmentManagerViewModel
所对应 子Fragment
的 ID,value 为 子Fragment
的 FragmentManagerViewModel
的 HashMap。
上面代码会根据本 Fragment
ID 去找 子Fragment
的 FragmentManagerViewModel
实例,如果有则存到 mChildNonConfigs
中,如果没有则构造一个新的实例存入到 mChildNonConfigs
中。
因此,如果一个 Fragment
下面有多个 子Fragment
,那么这些 子Fragment
所对应的 FragmentManagerViewModel
都是同一个,且存在 父Fragment.mChildNonConfigs
中。在上例中就是 FragmentActivity
下面有多个 根Fragment
,那么这些 根Fragment
所对应的 FragmentManagerViewModel
都是同一个,且都存在 FragmentActivity
的 FragmentManager.mChildNonConfigs
中。
所以 子Fragment
的 ViewModel
能够恢复的原因,与 根Fragment
的 ViewModel
能够恢复的原因是一样的:在 根Fragment
最初与 FragmentActivity
绑定时,根Fragment
就通过 performAttach
,在 根Fragment
对应的 FragmentManagerViewModel
中,以 根Fragment
ID 为 key,新构造的 FragmentManagerViewModel
为 value,初始化了 根Fragment
的 FragmentManagerViewModel.mChildNonConfigs
。之后 子Fragment
的 FragmentManagerViewModel
就会放在 mChildNonConfigs
中。
番外:理解 FragmentHostCallback 和 FragmentController
从 FragmentHostCallback
的类注释中我们可以知道,该类主要是为了面向 Fragment
宿主提供一个统一的 Fragment 相关信息的回调,包括 onGetLayoutInflater
,onShouldSaveFragmentState
等。这些回调有的来自 Fragment
类,有的来自 FragmentManager
类,因此 FragmentHostCallback
是 Fragment 向外暴露信息的回调的整合点。目前 FragmentHostCallback
的唯一实现类是 FragmentActivity
的内部类 HostCallbacks
。内部类可以直接调用它的外部类,这样,Fragment 的相关信息回调就通过 FragmentActivity.HostCallbacks
传给了 FragmentActivity
。
FragmentHostCallback
类不仅向宿主类提供 Fragment 的相关事件回调,它还持有一个 FragmentManager
的实例 mFragmentManager
,并且该实例在 FragmentHostCallback
中没有任何使用。我们查看 mFragmentManager
使用的地方可以发现都是在 FragmentController
中。
FragmentController
通过构造函数传入 FragmentHostCallback
的实例 mHost
,再通过 mHost
调用到 mFragmentManager
。我们看看 FragmentController
的方法,可以发现几乎都是宿主 FragmentActivity
主动调用的 生命周期方法 以及 需要从 Fragment 中获取数据的方法,例如获取 FragmentManager
实例。
网友评论