目录
问题
用过AAC的人都知道,ViewModel的使用是很简单的,没有太多复杂的东西,而ViewModel本身的源码也异常简单。真正“神奇”的地方在于ViewModel的生命周期:
在Activity的configuration(配置)发生变化时,Activity会被重建,但是ViewModel却不会,直至Activity真正消失的时候,ViewModel的onCleared方法才会被调用。这就是我们的核心问题,这一切是怎么实现的?
(源码版本androidx.lifecycle:lifecycle-viewmodel:2.2.0
)
1. ViewModel的获取
ViewModel本身是异常简单的:
public abstract class ViewModel {
protected void onCleared() {
}
}
ViewModel就是这样,仅仅包含一个onCeared
方法(最初的ViewModel的确只有这一个方法,后来又增加了一些方法,但是这些方法都不是给我们使用的,所以可以忽略)。
AAC给我们提供的获取ViewModel的方法很简单:
ViewModelProvider(activity/fragment).get(ViewModel.class)
(之前更常见的一种方式是ViewModelProviders.of(activity/fragment).get(ViewModel.class)
,但是现在这种方式已经被删除了)。
我们来看一下ViewModelProvider是怎么实现的:
@SuppressWarnings("WeakerAccess")
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
/**
* 创建ViewModel的Factory
*/
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
//这里的ViewModelStoreOwner 一般而言不是Activity就是Fragment,并且它们都实现了HasDefaultViewModelProviderFactory,也就是说Activity/Fragment都会提供默认的ViewModelFactory
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
/**
* @param store ViewModel存储的地方
* @param factory ViewModel构建的地方
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
/**
* 如果不提供key,默认的key就是ViewModel的类名(有前缀)
*/
@NonNull
@MainThread
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");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
/**
* 如果ViewModel在ViewModelStore中已经存在,则可以直接返回使用
* 如果不存在,则通过Factory新建,默认的工厂是SavedStateViewModelFactory,新建ViewModel的方式是反射
*/
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//给你一个机会再重新获取ViewModel时做一些操作
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
}
总结一下ViewModelProvider的作用。ViewModelProvider是我们获取ViewModel的地方,创建ViewModelProvider需要我们提供两个参数:1. ViewModelStore 2. ViewModelProvider.Factory。它们的作用也很明了,分别用来存储ViewModel和创建ViewModel。首先在ViewModelStore中查找,没有的话再通过Factory创建。
ViewModelProvider
的get
方法是可以提供一个key
来说明我们需要的是哪个ViewModel。不过,我们可能不常提供,不提供的话默认的key
是ViewModel的类名(前面加了一个前缀)。如果我们的Activity或者Fragment需要同一种ViewModel类的两个对象时,key
的作用就很明显了。
2. ViewModel的创建和存储
2.1 ViewModelProvider.Factory
默认的ViewModelProvider.Factory是SavedStateViewModelFactory
,从这个名字可以看出,这是一个可以为ViewModel提供SavedState的Factory,ViewModel的SavedState是2.x版本新添加的功能,目的是为了在ViewModel中方便的保存状态,在Activity重建的时候使用。不了解这些没关系,因为如果你不使用这个SavedState功能的话,这个SavedStateViewModelFactory
其实会退化成AndroidViewModelFactory
,它只是通过反射创建ViewModel而已。这要求我们的ViewModel必须有默认构造函数。从ViewModel的生命周期图上可以看出,ViewModel的生命周期要比Activity或者Fragment的更长,ViewModel中自然就不能包含Activity或者Fragment,如果实在需要在ViewModel中使用Context,那么只能使用ApplicationContext,为了方便我们使用,AAC中还定义了一个AndroidViewModel
:
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
我们的ViewModel也可以继承自AndroidViewModel
,这样在ViewModel中就可以方便地使用Application了。而AndroidViewModelFactory
也可以通过反射创建继承自AndroidViewModel
的ViewModel。
想了解更多关于ViewModel-SavedState的内容,可以查看这篇文章ViewModel如何保存状态——SavedState源码解析。
2.2 ViewModelStore
兼容包中的Activity和Fragment皆实现了ViewModelStoreOwner
接口,该接口只包含一个方法getViewModelStore
。
2.2.1 Activity与ViewModelStore
public class ComponentActivity implements ViewModelStoreOwner {
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
// 延迟创建,直到 getViewModelStore() 被调用时才会被创建
private ViewModelStore mViewModelStore;
/**
* 保留所有的 non-config state. 不随配置变化的那些变量。
* 最重要的就是保留了 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;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
/**
* 从 NonConfigurationInstances 中取出 viewModelStore
* 没有则新建一个 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) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
对于Activity而言,它本身就包含有一个ViewModelStore,在调用getViewModelStore
方法时会被创建(如果需要的话)。ViewModel最神奇的地方就是它不会随配置的变化而重建,表现在生命周期上就是文中的第一幅图。这一切是怎么实现的呢?在上述代码中我们已经可以看出来了。Activity的ViewModelStore会在配置发生变化时通过onRetainNonConfigurationInstance
方法被保留下来,在getViewModelStore
方法中又首先会从NonConfigurationInstances
中取出被保留下来的ViewModelStore(如果有的话)。这样,ViewModelStore就不会因为配置的变化而重建,也就是说,我们的ViewModel不受配置变化的影响。
2.2.2 Fragment与ViewModelStore
public class Fragment implements ViewModelStoreOwner {
//自身并没有实现,委托给了FragmentManager
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
}
再来看看FragmentManager
final class FragmentManagerImpl extends FragmentManager {
private FragmentManagerViewModel mNonConfig;
//又委托给了mNonConfig
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
/**
* 这个方法会在上述方法 getViewModelStore 之前被调用,是mNonConfig被赋值的地方
* 其中host一般情况下是我们的Activity
* parent是指父Fragment
*/
public void attachController(@NonNull FragmentHostCallback host,
@NonNull FragmentContainer container, @Nullable Fragment parent) {
if (mHost != null) throw new IllegalStateException("Already attached");
mHost = host;
mContainer = container;
mParent = parent;
if (parent != null) { //父Fragment不为null,则从父Fragment的FragmentManager中获取NonConfig
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
//host是我们的Activity,它的确实现了ViewModelStoreOwner接口
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
}
Fragment将getViewModelStore
委托给了其FragmentManager,FragmentManager又把getViewModelStore
委托给了NonConfig。从NonConfig这个名字可以猜测出,它应该是保存Fragment中与配置无关的变量的地方(比如说我们的ViewModelStore)。而NonConfig的类型却是FragmentManagerViewModel
,用一个ViewModel去保存ViewModelStore,这是什么骚操作?
看看FragmentManagerViewModel就知道了
/**
* FragmentManagerViewModel is the always up to date view of the Fragment's non configuration state
* FragmentManagerViewModel保存了最新的Fragment的non configuration state
*/
class FragmentManagerViewModel extends ViewModel {
private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
@NonNull
@Override
@SuppressWarnings("unchecked")
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
return (T) viewModel;
}
};
/**
* 从上一段代码中可以看出,此处的ViewModelStore其实就是Activity中的ViewModelStore
* 也就是说这个FragmentManagerViewModel是被保存在Activity的ViewModelStore中的
*/
@NonNull
static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
FACTORY);
return viewModelProvider.get(FragmentManagerViewModel.class);
}
//保存子Fragment的NonConfig,对于我们而言,其实就是保存子Fragment的ViewModelStore
private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
//保存Fragment的ViewModelStore
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
@NonNull
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;
}
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}
}
如注释所说,FragmentManagerViewModel的作用是保存Fragment最新的non configuration state,我们要保存的Fragment的ViewModelStore也是non configuration state,保存在FragmentManagerViewModel中正合适。但是,FragmentManagerViewModel是怎么做到这一点的呢?答案很简单,把FragmentManagerViewModel放到Activity的ViewModelStore中,我们上面已经分析过了,Activity的ViewModelStore在配置发生变化时会被保留下来,所以FragmentManagerViewModel自然也就不受配置变化的影响。想想也是,FragmentManagerViewModel也就是个普通的ViewModel,自然拥有ViewModel的特点,只不过AAC正好利用了这一点,把它用来保存Fragment的non configuration state(我们最关心的Fragment的ViewModelStore也在其中)。
Activity的ViewModelStore是不随配置变化的,Fragment的ViewModelStore也自然不随配置变化。
2.2.3 ViewModelStore
说了这么多ViewModelStore,到底ViewModelStore长啥样?显然ViewModelStore就是存储ViewModel的地方。如前所述,我们实际上是通过字符串key
去获取的ViewModel,那么ViewModelStore自然是以Map的形式来存储ViewModel。嗯,基本上这就是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());
}
/**
* 会在Activity、Fragment destroy并且不是因为配置发生变化的时候被调用
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
3. 隐藏技能
- 如果我们需要在同一个Activity或者Fragment中创建多个同一类型的ViewModel,我们可以在获取ViewModel的时候用不同的
key
进行区分。 - 默认情况下
AndroidViewModelFactory
会帮我们以反射的方式创建ViewModel,但是ViewModel是在View和Model之间起到桥梁的作用,ViewModel一般情况下都需要包含Model层,而使用AndroidViewModelFactory
又要求我们的ViewModel必须包含默认构造函数(AndroidViewModel情况除外),这就导致我们不能使用构造函数的方式去注入Model层,有时候这很不方便。还好,我们可以提供自己的ViewModelProvider.Factory
。恰好,dagger2有一种能力叫作MultiBindings,是以依赖注入创建ViewModelProvider.Factory
的绝佳方式,详见当Dagger2撞上ViewModel。 - 你可能觉得Activity的
onRetainNonConfigurationInstance
是个保存NonConfig State的好地方,如果我们也有什么不随配置变化的变量,也可以利用onRetainNonConfigurationInstance
来保存,甚至Activity中有个方法叫onRetainCustomNonConfigurationInstance
,专门帮我们保存自己的NonConfig State。恭喜你,从源码中又学到了一点没有卵用的东西。Activity觉得你很聪明,并向你扔出一个Deprecated警告。onRetainCustomNonConfigurationInstance
方法已经被标记为废弃的,并推荐你使用ViewModel来保存NonConfig State。至于原因,不言自明。
4. 总结
ViewModel要解决的核心问题是怎样在配置发生变化时保留ViewModel。利用Activity提供的onRetainNonConfigurationInstance
方法,可以方便的保留不受配置变化影响的数据。
在实现了Activity的ViewModelStore之后,我们便可以利用ViewModel本身的特点去保存Fragment的ViewModelStore,如此这般,Fragment的ViewModel便也不再受配置变化的影响。用Activity中的ViewModelStore去保存FragmentManagerViewModel,而FragmentManagerViewModel又保存了Fragment的ViewModelStore。嗯,真真是踩在自己的肩膀上向上爬。
网友评论