同时,Google官方发起ViewModel只管包管 纯的业务代码,不要持有任何View层(Activity大概Fragment)或Lifecycle的引用,这样包管了ViewModel内部代码的可测试性,避免因为Context等相干的引用导致测试代码的难以编写(比如,MVP中Presenter层代码的测试就需要额外成本,比如依赖注入大概Mock,以包管单位测试的举行)。
3.2 更便于生存数据
由体系响应用户交互大概重建组件,用户无法操控。当组件被烧毁并重建后,原来组件相干的数据也会丢失——最简单的例子就是屏幕的旋转,如果数据类型比力简单,同时数据量也不大,可以通过onSaveInstanceState()存储数据,组件重建之后通过onCreate(),从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。
ViewModel的扩展类则会在这种情况下自动保存其数据,如果Activity被重新创建了,它会收到被之前相同ViewModel实例。当所属Activity终止后,框架调用ViewModel的onCleared()方法开释对应资源:
这样看来,ViewModel是有肯定的 作用域 的,它不会在指定的作用域内生成更多的实例,从而节省了更多关于 状态维护(数据的存储、序列化和反序列化)的代码。
ViewModel在对应的 作用域 内保持生命周期内的 局部单例,这就引发一个更好用的特性,那就是Fragment、Activity等UI组件间的通信。
3.3 更方便UI组件之间的通信
一个Activity中的多个Fragment相互通讯是很常见的,如果ViewModel的实例化作用域为Activity的生命周期,则两个Fragment可以持有同一个ViewModel的实例,这也就意味着数据状态的共享:
public class AFragment extends Fragment {
private CommonViewModel model;
public void onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(CommonViewModel.class);
}
}
public class BFragment extends Fragment {
private CommonViewModel model;
public void onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(CommonViewModel.class);
}
}
上面两个Fragment getActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个ViewModel。
我不知道正在阅读本文的您,有没有冒出这样一个想法:
ViewModel提供的这些特性,为什么感觉互相之间没有联系呢?
这就引发下面这个标题,那就是:
这些特性的本质是什么?
4. ViewModel:对状态的持有和维护
ViewModel层的根本职责,就是负责维护UI的状态,追根究底就是维护对应的数据——毕竟,无论是MVP还是MVVM,UI的展示就是对数据的渲染。
- 1.界说了ViewModel的基类,并发起通过持有LiveData维护生存数据的状态;
- 2.ViewModel不会随着Activity的屏幕旋转而烧毁,减少了维护状态的代码成本(数据的存储和读取、序列化和反序列化);
- 3.在对应的作用域内,保正只生产出对应的唯一实例,多个Fragment维护相同的数据状态,极大减少了UI组件之间的数据传递的代码成本。
现在我们对于ViewModel的职责和思想都有了肯定的相识,按理说接下来我们应该阐述怎样使用ViewModel了,但我想先等等,因为我觉得相比API的使用,把握其本质的思想会让你在接下来的代码实践中如鱼得水。
不,不是源码剖析…
通过库提供的API接口作为开始,阅读其内部的源码,这是尺度把握代码内部原理的思路,这种方式的时间成本极高,即使有相干源码分析的博客举行引导,文章中大片大片的源码和注释也足以让人望而却步,于是我理所固然这么想:
先学会怎么用,再抽空体系学习它的原理和思想吧…
发现没有,这和上学时候的学习方式竟然截然相反,甚至说本末倒置也不希奇——任何一个物理大概数学公式,在使用它做题之前,对它背后的底子理论都应该是优先去体系性学习把握的(比如,数学公式的学习一样平常都需要先通过肯定方式推导和证明),这样我才能拿着这个知识点对课后的习题举一反三。这就好比,如果一个老师直接告诉你一个公式,然后啥都不说让你做题,这个老师肯定是不合格的。
我也不是很喜好大篇幅地复制源码,我准备换个角度,站在Google工程师的角度看看怎么样设计出一个ViewModel。
站在更高的视角,设计ViewModel
现在我们是Google工程师,让我们再回首一下ViewModel应起到的作用:
- 1.规范化了ViewModel的基类;
- 2.ViewModel不会随着Activity的屏幕旋转而烧毁;
- 3.在对应的作用域内,保正只生产出对应的唯一实例,包管UI组件间的通信。
1.设计基类
这个简直太简单了:
public abstract class ViewModel {
protected void onCleared() {
}
}
我们界说一个抽象的ViewModel基类,并界说一个onCleared()方法以便于开释对应的资源,接下来,开辟者只需要让他的XXXViewModel继续这个抽象的ViewModel基类即可。
2.包管数据不随屏幕旋转而烧毁
这是一个很神奇的功能,但它的实现方式却非常简单,我们先相识这样一个知识点:
setRetainInstance(boolean) 是Fragment中的一个方法。将这个方法设置为true就可以使当前Fragment在Activity重建时存活下来
这似乎和我们的功能非常吻合,于是我们不禁这样想,可不可以让Activity持有这样一个不可见的Fragment(我们干脆叫他HolderFragment),并让这个HolderFragment调用setRetainInstance(boolean)方法并持有ViewModel——这样当Activity因为屏幕的旋转烧毁并重建时,该Fragment存储的ViewModel天然不会被随之烧毁回收了:
public class HolderFragment extends Fragment {
public HolderFragment() { setRetainInstance(true); }
private ViewModel mViewModel;
// getter、setter…
}
固然,思量到一个复杂的UI组件大概会持有多个ViewModel,我们更应该让这个不可见的HolderFragment持有一个ViewModel的数组(大概Map)——我们干脆封装一个叫ViewModelStore的容器对象,用来承载和代理所有ViewModel的管理:
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
// put(), get(), clear()…
}
public class HolderFragment extends Fragment {
public HolderFragment() { setRetainInstance(true); }
private ViewModelStore mViewModelStore = new ViewModelStore();
}
好了,接下来需要做的就是,在实例化ViewModel的时候:
1.当前Activity如果没有持有HolderFragment,就实例化并持有一个HolderFragment 2.Activity获取到HolderFragment,并让HolderFragment将ViewModel存进HashMap中。
这样,具有生命周期的Activity在旋转屏幕烧毁重建时,因为不可见的HolderFragment中的ViewModelStore容器持有了ViewModel,ViewModel和其内部的状态并没有被回收烧毁。
这需要一个条件,在实例化ViewModel的时候,我们似乎还需要一个Activity的引用,这样才能包管 获取大概实例化内部的HolderFragment并将ViewModel举行存储。
于是我们设计了这样一个的API,在ViewModel的实例化时,加入所需的Activity依赖:
CommonViewModel viewModel = ViewModelProviders.of(activity).get(CommonViewModel.class)
我们注入了Activity,因此HolderFragment的实例化就交给内部的代码执行:
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = createHolderFragment(fm);
return holder;
}
这之后,因为我们传入了一个ViewModel的Class对象,我们默认就可以通过反射的方式实例化对应的ViewModel,并交给HolderFragment中的ViewModelStore容器存起来:
public T get(Class modelClass) {
// 通过反射的方式实例化ViewModel,并存储进ViewModelStore
viewModel = modelClass.getConstructor(Application.class).newInstance(mApplication);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
3.在对应的作用域内,保正只生产出对应的唯一实例
怎样包管在不同的Fragment中,通过以下代码生成同一个ViewModel的实例呢?
public class AFragment extends Fragment {
private CommonViewModel model;
public void onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(CommonViewModel.class);
}
}
public class BFragment extends Fragment {
private CommonViewModel model;
public void onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(CommonViewModel.class);
}
}
实在很简单,只需要在上一步实例化ViewModel的get()方法中加一个判断就行了:
public T get(Class modelClass) {
// 先从ViewModelStore容器中去找是否存在ViewModel的实例
ViewModel viewModel = mViewModelStore.get(key);
// 若ViewModel已经存在,就直接返回
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
}
// 若不存在,再通过反射的方式实例化ViewModel,并存储进ViewModelStore
viewModel = modelClass.getConstructor(Application.class).newInstance(mApplication);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
现在,我们乐成实现了预期的功能——究竟上,上文中的代码正是ViewModel官方核心部分功能的源码,甚至默认ViewModel实例化的API也没有任何改变:
CommonViewModel viewModel = ViewModelProviders.of(activity).get(CommonViewModel.class);
固然,因为篇幅所限,我将源码举行了简单的删减,同时没有报告构造方法中带参数的ViewModel的实例化方式,但对于目前已经把握了设计思想和原理的你,学习这些API的使用几乎不费吹灰之力。
总结与思考
ViewModel是一个设计非常精巧的组件,它功能并不复杂,相反,它简单的难以置信,你甚至只需要相识实例化ViewModel的API怎样调用就行了。
同时,它的背后掺杂的思想和理念是值得去反复揣度的。比如,怎样包管对状态的规范化管理?怎样将纯粹的业务代码通过精良的设计下沉到ViewModel中?对于非常复杂的界面,怎样将各种各样的功能抽象为数据状态举行解耦和复用?随着MVVM开辟的深入化,这些标题都会一个个浮出水面,这时候ViewModel组件精良的设计和这些不起眼的小特性就随时有大概成为璀璨夺目的闪光点,帮你攻城拔寨。
--------------------------广告分割线------------------------------
系列文章
争取打造 Android Jetpack 讲解的最好的博客系列:
- Android官方架构组件Lifecycle:生命周期组件详解&原理分析
- Android官方架构组件ViewModel:从前世今生到追本溯源
- Android官方架构组件LiveData: 观察者模式领域二三事
- Android官方架构组件Paging:分页库的设计美学
- Android官方架构组件Paging-Ex:为分页列表添加Header和Footer
- Android官方架构组件Paging-Ex:列表状态的响应式管理
- Android官方架构组件Navigation:大巧不工的Fragment管理框架
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提拔技能,每每是本身探索发展大概是报班学习,但对于培训机构动则近万的学费,着实压力不小。本身不成体系的自学效果低效又漫长,而且极易碰到天花板技术故步自封!
因此网络整理了一份《2024年Android移动开辟全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提拔又不知道该从何学起的朋侪,同时减轻大家的负担。
既有得当小白学习的零底子资料,也有得当3年以上履历的小伙伴深入学习提拔的进阶课程,根本涵盖了95%以上Android开辟知识点,真正体系化!
由于文件比力大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习条记、源码讲义、实战项目、讲解视频,并且会连续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
最后这里放上我这段时间复习的资料,这个资料也是偶尔一位朋侪分享给我的,里面包含了腾讯、字节跳动、阿里、百度2019-2021口试真题剖析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提拔进阶,也可以分享给身边好友一起学习。
一起互勉~
《互联网大厂口试真题剖析、进阶开辟核心学习条记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
构技术进阶脑图、高级进阶架构资料** 帮助大家学习提拔进阶,也可以分享给身边好友一起学习。
[外链图片转存中…(img-gFTBXztJ-1712391835801)]
[外链图片转存中…(img-kMoeaZpA-1712391835801)]
一起互勉~
《互联网大厂口试真题剖析、进阶开辟核心学习条记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |