往期回顾,专栏目录,更新动态,优惠政策,版权须知
温馨提示:如这是第一次接触《重学安卓》,可通过上述链接快速了解《重学安卓》专栏,获取 专栏目录、试读内容、更新动态 和 发展状况。
截至目前,专栏已对 体系化文章 实施 3310 余次修订,数十位群友告诉我,受专栏启发 亦开启了写作之路。群里不定期会有小伙伴讨论适配问题、分享原创开源库 和 提供内推机会,订阅后可随时进群交流。
·
Note 2023.4.9 新版本提示:
本文最初发行于 2019.7.17,在此期间,我们从未停下分享和交流的脚步,
为此,一方面,我们不断为 ViewModel 篇提供加餐和翻新,另一方面,不断萌生和沉淀新的见解,
时隔本文发行 3 周年零 8 个月,我们发行了本文的 “重制版” ——《重学安卓:再回首 页面搭档 Jetpack ViewModel》,通过 “循序渐进、言简意赅” 的叙述方式,重新阐述对 ViewModel 的完整理解,
建议以 “重制版” 内容为准,而打满细节补丁的本文,则可作为 “工具书” 来查阅和参考。
·
Note 2021.5.25 重要提示:
阅读本文最佳时机是,您已吃过 阅读 “源码” 或 “源码分析文” 时 找不到头绪的苦。
您还没吃过苦,那您先不要着急阅读本文。您得吃过苦,才会有体会。
在您吃够这方面苦后,您才有机会发现,本文正是专用于解决 “如何找到正确打开方式” 的困扰。
我们绝不通篇贴源码,而是基于广泛实践和反思,在累积过大量样本 乃至足以排除掉所有干扰信息后,点到为止揭露 ViewModel 框架最核心本质,方便您理解其真实的存在意义,乃至笃信使用项目中。
关于 ViewModel 在项目中实践,请另行查阅《GitHub : Jetpack-MVVM-Best-Practice》源码以及《架构模式实践》篇 完整解析。
前言
上一期,我们以现实中 “音乐播放器” 开发为例,方便大家体会为何存在 LiveData 这样设计 —— 它究竟是在怎样背景下、为解决怎样问题而生。
相信阅读过上一期朋友,对 LiveData 存在缘由、目标使命、职责边界 有了印象。
且就算将来不用 LiveData,也能因 LiveData 树立起的 通过 “唯一可信源” 完成状态分发 理念,减少不可预期错误,从而让开发工作舒畅自如。
被严重低估的 Jetpack ViewModel
提到 ViewModel,大部分人都会觉得 “没啥可说”。或许他们对 ViewModel 认知,还停留在 MVVM - Clean 时代,或干脆误以为是 Presenter 般存在。
这使 ViewModel 价值,未真正被重视和利用起来。
Jetpack ViewModel 果真如某些人所说,是 MVP Presenter 阉割版,或 Clean ViewModel 的存在么?
答案是否定的。
作为 面向成熟、标准化、规范化架构设计 背景下诞生的 Jetpack ViewModel,绝不可与 Presenter 或是 Clean ViewModel 同日而语。
Jetpack ViewModel 在实现状态管理框架 单向依赖 理念中,有不可替代作用。
所以长话短说,让我们来一起领略一下,在 Jetpack ViewModel 问世前和问世后,软件开发发生怎样剧变。
注:“Jetpack ViewModel” 下文皆以 “ViewModel” 指代。
文章目录一览
- 前言
- 被严重低估的 Jetpack ViewModel
- ViewModel 目标只有三个
- ViewModel 问世前的混沌世界
- ViewModel 为何能解决这三个问题?
- Note 2020.8.9 加餐:
- 对作用域机制本质描述简化重制
- 引入 ViewModel 后的世界
- Note 2021.07.18 加餐:
- “状态管理一致性问题” 解析及示例补充
- Note 2020.10.28 加餐:
- 解析 ViewModel 在实际开发中 “两大高频场景” 下使用
- 需要明确的 3 个小细节
- Note 2020.2.10 加餐:
- 对 SavedStated 被单独抽取维护缘由解析
- Note 2021.07.20 加餐:
- ViewModel 业务能力外场景及应对方案
- 综上
ViewModel 目标只有三个
ViewModel 最早于 2017 年 Google I/O 大会上被提出,同时间问世还有 Lifecycle 和 LiveData。
谈论 ViewModel 时常常避不开 LiveData。LiveData 就像 ViewModel 左臂右膀,支撑 ViewModel 作为中间层,对状态管理尽 “承上启下” 职责。
所以尽管本文目标是介绍 ViewModel 存在缘由、设计依据、职责边界,本着介绍 “相互间关系” 需要,还是会顺带展示 LiveData 身影。
ViewModel 目标只有三个:
1.让状态管理独立于视图控制器,从而实现 “状态管理分治”、“状态管理一致性” 和 “状态共享”(跨页面通信)。
2.为状态设置作用域,使状态共享做到作用域可控。
3.实现单向依赖,避免内存泄漏。
上述三点,如暂无体会,那接下来,请跟随我脚步铺垫 ViewModel 来龙去脉。
ViewModel 问世前的混沌世界
例如我们开发一款单 Activity 地图应用。需求是:
1.地图作为背景供全局查看和操作。
2.当前页面皆以 Fragment 形式展示和管理。
2.1.其中 ListFragment(列表页)持有一级图形工具单例 ListGraphicManager,可对地图上图斑进行选中和跳转到详情页。
2.2.List 中每个 item 可跳转到 DetailFragment(详情页),详情页持有二级图形工具单例 DetailGraphicManager,可对地图上图斑进行选中和编辑轮廓。并在退出详情页时重置二级图形工具中状态数据。
值得注意的是,操作图斑导致图斑变化,需及时反映在 Fragment。因而此前不仅是 Fragment 去引用 GraphicManager,GraphicManager 也反过来持有 Fragment 引用。
这会造成什么问题?
首先,由于图形工具生命周期比 Fragment 长,当 Fragment destroy 时,因被图形工具持有,而可能造成内存泄漏。
并且,列表页和详情页之间如要通信,只能另编写接口,通过 Activity 中转,这样项目中便产生大量接口。
如不使用接口,而是用 EventBus,就易导致 LiveData 篇所述,因 缺失 “唯一可信源理念” 约束 造成状态分发滥用,而指数级增长不可预期错误。
再者,如页面彼此间不能共享数据,就需编写大量重复代码,例如 ListPresenter 和 DetailPresenter 包含某些同样请求和逻辑,却因隶属不同页面,而不得不将同样参数注入两份,并额外增加状态管理负担(例如页面退出时,你需手动一个个将状态对象置空,不然有内存泄漏风险)。
最后,在屏幕旋转并发生重建后,Presenter 因直接隶属 Activity 而同样被销毁,起不到存储大数据供视图控制器恢复状态作用,状态恢复全靠视图控制器重建机制支撑。
而该重建机制设计目标,本是支持 “轻量级状态” 存储与恢复,“重量级状态” 势必影响效率,而可能发生主线程阻塞,从而造成旋屏时卡顿。