ViewModel 的设计与常用用法。
| ViewModel 用法 | 原理 |
|---|---|
| ViewModel | 这个主要就是提供我们存储东西的类,跟随生命周期去做释放等 |
| ViewModelStore | 提供 ViewModel 的存储,一个 Store 支持按照 Key 存多个 ViewModel |
| ViewModelStoreOwner | 该 Store 所属于的角色,例如 是 Fragment 还是 Activty |
| ViewModelProvider | 需要传入对应的 Store |
| Factory | 创建一个 ViewModel |
| NonConfigurationInstance | 对 ViewModelStore 的存储与恢复,最终在系统的 Activity 以及 ActivityThread 里面驱动保存和恢复的,这里需要看系统源码。 |
流程分析:
// 入口,我们使用的时候怎么获取到一个 ViewModel
private val viewModel: GrowthViewModel by lazy { ViewModelProvider(this).get(GrowthViewModel::class.java) }
// 这里是 kotlin 写法,使用 lazy 代理方式去加载,里面传入的是一个 ViewModelStoreOwener */
// get 方法,首先判断 store 里面有就直接返回,否则使用 factory 创建并且放到 store 里面
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 这里如果是 Fragment 共用 Activity 的 ViewModel 这里的 Key 是一样的,所以复用就保证了 ViewModel 的一致性,因此,在 Fragment 里面彼此之间传递数据用这种方式更好。
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
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);
}
// 这里就是存到 store 的 map 里面,以 生成的 String 为 key viewModel 作为 value
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
// FragmentActivity 的 getViewModelStore,调用到父类的 方法里
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 里面取
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
// 陷入到 Activity 里面
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
// attach 里面拿到这个
mLastNonConfigurationInstances = lastNonConfigurationInstances;
// 经过上次的追踪,是系统帮我们存起来的这个 NonConfiguration 里面的,onAttach 的时候放入就置空,为了有效性
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
总结
ViewModel 怎样帮我们做到了数据分离呢,主要就是因为系统给我们提供了存储和取用,方便了我们生命周期的管理,ViewModel 并没有生命感知能力,但是却可以保证很快的把这个 Model 给我们,那么我们无论从中获取 LiveData 还是获取一些自定义的数据,例如普通数据,Flow 等等,都是可以的。 ViewModel 怎样保证了数据的单向流动,就是我们 UI 只是依赖他们,但是他们并不会持用我们 UI 任何东西,这样能避免 UI 上的依赖和互相引用的问题,这样不会在 UI 哪里去让请求网络或者 IO 的时候匿名内部类去持有 UI 承载,另外,还有一个目的就是做到职责分离,在 ViewModel 里面通过 Repository 层或者自己去请求和处理数据,那么这会的触发和处理都下放到 ViewModel 及其后面的一系列流程中了,这样就能做到 UI 和 数据 的分离。我们的设计上也应该遵循这些原则,既然谷歌给提供了这些便利的组件,那我们就直接用,在设计别的方面的时候我们要借鉴。 这里如果配合 Kotlin 的委托 lazy 等方式,直接就可以做到依赖注入的类似效果了。