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的实例
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
上面分享的百度、腾讯、网易、字节跳动、阿里等公司2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
【Android高级架构视频学习资源】
PDF+学习视频+面试文档+知识点笔记](https://bbs.csdn.net/topics/618156601)
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
[外链图片转存中…(img-ssFSPRE5-1711350702468)]
【Android高级架构视频学习资源】
**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
标签:插件,HolderFragment,Fragment,ViewModel,追本溯源,class,Activity,public From: https://blog.csdn.net/2301_79058150/article/details/137015088