1. 从 ViewModelProvider(this).get(xxViewModel.class) 讲起
首先,我们看看 ViewModelProvider
的构造函数:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
上面代码我们有两个地方需要搞清楚,ViewModelStore
和 ViewModelProvider.NewInstanceFactory
。大体上我们可以猜测到,前者是存储 ViewModel
实例的,后者是构造 ViewModel
实例的工厂。
1.1 ViewModelStore(存储 ViewModel 的实例)
ViewModelProvider
的构造函数接收一个 ViewModelStoreOwner
,而 ViewModelStoreOwner
接口只有一个 getViewModelStore
方法,它返回一个 ViewModelStore
。所以我们看看 ViewModelStore
(顾名思义可以猜到它就是用来存储 ViewModel
实例的):
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
的实例。
有点类似
ThreadLocal
的实现原理,本质上是有一个Entry
,它存储了ThreadLocal
与value
的映射
因此,ViewModelStore
的职责是保存 ViewModel
的实例,而 ViewModelStore
的 owner 类,则必须保证在发生配置改变时,发生重建后,依然返回同一个 ViewModelStore
实例(这样,我们就可以返回同一个 ViewModel
)。我们的 ComponentActivity
就是一个 ViewModelStoreOwner
,我们在后面小节将会看看在重建后,它是如何返回同一个 ViewModelStore
实例的。
1.2 ViewModelProvider.Factory(构造 ViewModel 实例的工厂方法)
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
。
2. 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;
}
从上面可以看到,当我们调用 new ViewModelProvider(this).get(xxViewModel.class);
来实例化并获取 xxViewModel
实例时,其实就是从 1.1 的 ViewModelStore
中去取,如果没有就用 1.2 的 newInstanceFactory
新构造一个,并放入 1.1 的 ViewModelStore
以方便以后获取同一个实例。
所以,配置重建后,能够获取同一个 ViewModel
实例的关键在于能够获取到同一个 ViewModelStore
的实例。
3. Activity 中 ViewModel 的保存和恢复
上一节中我们说了,ViewModel
的保存和恢复是本质上依赖于 ViewModelStore
的保存和恢复。而 ViewModelStore
的保存和恢复则是利用了 Activity.onRetainNonConfigurationInstance
机制。
3.1 ViewModelStore 的保存
我们来看支持 ViewModel
机制的 ComponentActivity.onRetainNonConfigurationInstance
中是如何保存 ViewModelStore
的:
@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;
}
从上面我们可以看到,ViewModelStore
的保存是依赖于 onRetainNonConfigurationInstance
的,在 onRetainNonConfigurationInstance
回调时,构建了新的 NonConfigurationInstance
实例,并将现在的 ViewModelStore
赋值给它的 nci.viewModelStore
属性。
3.2 ViewModelStore 的恢复
ViewModelStore
和 ViewModel
的恢复其实就是当我们使用 ViewModelProvider
来获取 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
。
4. onRetainNonConfigurationInstance 机制
通过上面的分析,我们知道,保存恢复 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
在 Activity.onDestroy()
销毁时,通过 onRetainNonConfigurationInstance
时的回调被保存在了 NonConfigurationInstances
实例中,该实例被 ActivityThread
持有。下次 Activity
重建时,由 ActivityThread.performLaunchActivity
方法中调用 Activity.attach
方法,再将 NonConfigurationInstances
实例传给重建后的 Activity
。
5. 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
。
6. 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);
由上面的 (2) 我们知道,创建和获取 ViewModel
的关键在于 getViewModelStore
方法,而这个方法的实现上,Activity
和 Fragment
是不相同的:
-
Fragment
的getViewModelStore
是从FragmentManager.getViewModelStore
中获取的。 -
Activity
的getViewModelStore
是从ComponentActivity.getViewModelStore
获取的,它是直接存储在mViewModelStore
中的(根据 (4),mViewModelStore
是在Activity
attach 时,由ActivityThread
传入的)。
因此我们下面分析的重点应该在于 FragmentManager.getViewModelStore
的机制。
6.1 FragmentActivity 在初始化时做了什么
Fragment
是依附于 FragmentActivity
的,因此对 FragmentManager
的操作一定也是通过 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) { // FragmentActivity 中传入的 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
(是的,HostCallbacks
就是绕了一下而已),因此上面代码创建的实例 mNonConfig: FragmentManagerViewModel
,其实存入到了两个地方:
1. FragmentActivity
的 ViewModelStore
中,所以 FragmentActivity.mViewModelStore
中不仅有 Activity
环境下创建的 ViewModel
,还包含了一个(或多个)根 Fragment
对应的 FragmentManagerViewModel
。
2. FragmentManager.mNonConfig
(注意 mNonConfig
是一个 FragmentManagerViewModel
的实例) 中
这两点很重要。
6.2 在根 Fragment 中获取 ViewModel
我们知道关键点在于调用的 Fragment.getViewModelStore
:
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
// 这里的 mFragmentManager 实例就是 FragmentActivity.HostCallbacks 内部类中的 mFragmentManager,
// 也就是在 (6.1) 节中我们提到拥有 FragmentManagerViewModel 的那个 FragmentManager
return mFragmentManager.getViewModelStore(this);
}
// FragmentManager 👇
ViewModelStore getViewModelStore(@NonNull Fragment f) {
// 这里的 mNonConfig 根据 (6.1) 节,是一个 FragmentManagerViewModel 的实例
return mNonConfig.getViewModelStore(f);
}
从上面代码和注释中我们可以知道
- 根
Fragment
的getViewModelStore
最终是从FragmentManagerViewModel.getViewModelStore
中获取的。这与FragmentActivity: ComponentActivity
的getViewModelStore
来源不一样,因此在FragmentActivity
和Fragment
中都获取同样类型(类名一样)的ViewModel
,获取出来的实例却是不一样的。 - 根据 (6.1) 我们可以知道,根
Fragment.getViewModelStore
的最终调用处FragmentManagerViewModel.getViewModelStore
中的FragmentManagerViewModel
实例,其实在FragmentActivity.onCreate
时就已经存入了FragmentActivity
的ViewModelStore
中。也就是说它们之间的结构是下面这样的:
FragmentManagerViewModel 与 Activity 的关系
所以,Activity
中创建的 ViewModel
是来自于它的 mViewModelStore
。而根 Fragment
中创建的 ViewModel
是来自于它的宿主 Activity
的 mViewModelStore.FragmentManagerViewModel.mViewModelStores
。
这里
mViewModelStores
是复数的原因是,一个Activity
可能有多个根Fragment
。
6.3 Fragment 创建时做了什么
从 (6.2) 最后的结论我们知道,FragmentActivity
在 onCreate
时就做好了如下引用链:activity: FragmentActivity
→ mViewModelStore: ViewModelStore
→ 里面有一个 key 对应的是 FragmentManagerViewModel
→ FragmentManagerViewModel.mViewModelStores
。
Q: 那么 根Fragment
是否也对 子Fragment
做了同样的准备?
各个层级的 Fragment
与它对应的 FragmentManager
有如下对应关系:
-
根Fragment
→FragmentActivity.FragmentController.mHost.mFragmentManager
以及它自己的mFragmentManager: FragmentManagerImpl
。 -
子Fragment
→父Fragment.getChildFragmentManager
以及 它自己的mFragmentManager: FragmentManagerImpl
。
当 根Fragment
关联到 Activity
上时,会调用其 performAttach
方法:
// androidx.fragment.app.Fragment
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()");
}
}
之后,它们通过各自的 FragmentManager
操作,在各层级的 FragmentManagerViewModel
之间建立了如下图的关系。
所以,各层级的
Fragment
的 ViewModel
能够恢复,也是受益于 Activity
中 ViewModelStore
的恢复。
番外:理解 FragmentHostCallback 和 FragmentController
从上面的代码中,我们可以注意到在 FragmentActivity
中有一个内部类 HostCallbacks
,这个类继承自 FragmentHostCallback
,但是它的所有实现方法都只是调用它的外部类 FragmentActivity
的相应方法而已,它自己没有任何实质的操作。
从 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
实例。