往期回顾,专栏目录,更新动态,优惠政策,版权须知
温馨提示:如这是第一次接触《重学安卓》,可通过上述链接快速了解《重学安卓》专栏,获取 专栏目录、试读内容、更新动态 和 发展状况。
截至目前,专栏已对 体系化文章 实施 3310 余次修订,数十位群友告诉我,受专栏启发 亦开启了写作之路。群里不定期会有小伙伴讨论适配问题、分享原创开源库 和 提供内推机会,订阅后可随时进群交流。
·
Note 2023.5.13 提示:
本文关注的是《Jetpack MVVM 脚手架》项目中各组件的 “使用细节”,
如对业务架构 “整体流程” 感兴趣,可移步《Jetpack MVVM 分层设计解析》篇 查阅。
Note 2022.6.10 提示:
由于 “Jetpack MVVM 示例项目” 的细节仍在持续迭代中,因而最佳方式是,不定期 pull 该项目最新源码,根据 git commit 记录了解最新设计及变动缘由。
Note 2021.8.27:
经调研,70% 以上公司仍在使用 Java 开发或维护项目,而 Java 项目又是 “Null 安全” 等 “一致性问题” 高发场景,因而关于 Jetpack MVVM,我们专注且只分享以 Java 语言为背景的 “通过架构组件 解决一致性问题” 的案例。
Kotlin 官方推广已有 4 年,有条件请及早上车,以及尝试 Compose 等框架。
Kotlin 中的 Android 基础知识 | Android Developers (google.cn)
前言
随着《Jetpack MVVM 精讲》发行及《Jetpack-MVVM-Best-Practice》开源,人们对 “架构模式” 项目接触与日俱增,
为此有小伙伴问,能否以 KunMinX 视角出一期 “全流程攻略”,以便留意到 “项目源码中” 之前未能留意到细节和用意。
答案是肯定的。
背景
人,不是机器,人注定会犯错。
尤其 多人协作背景下 快速版本迭代时。
有限注意力应始终放刀刃上,因而那些机械重复模板代码,应在后台自己默默安排好一切、免除因各种手工操作失误 带来不可预期隐患。
架构模式应运而生。
脚手架项目由来
如无特别说明,本专栏谈论 “架构”,均特指 “业务架构”,也即在 “业务开发” 背景下,以 “规避不可预期错误” 为目的展开的 “架构组件选型、设计及应用”。
经过 1 年多维护,作为《最佳实践》项目 Canary 版《Jetpack MVVM 脚手架》工程已趋于成熟,因而今天我们破例通过 “贴代码” 方式来给大家介绍:为快速、稳定、不出预期外错误开发,脚手架工程中分别应用哪些结构设计,并开源和维护哪些定制组件。
考虑到其间提及 “业务开发” 背景下存在的 高频隐患,因而就算不上手 Jetpack MVVM,也请务必理解这些隐患,以便遭遇时有 “脑回路” 可做出准确判断。
文章目录一览
- 前言
- 背景
- 脚手架项目由来
- 架构图总览
- 福利 1:DataBinding 严格模式
- 福利 2:Dispatcher 回推一次性消息
- 福利 3:Smooth-Navigation 使转场顺滑
- 细节 A:通过 State-Holder 托管和恢复状态
- 细节 B:通过 Dispatcher 跨页面通信和 “消息鉴权”
- 细节 C:通过 Request 来复用业务逻辑
- 细节 D:通过 UseCase 管理可叫停业务
- 细节 E:通过 DataResult 回调数据层结果
- Note 2020.12.01 加餐
- 对 DataResult v1.1 新设计补充说明
- 综上
架构图总览
《Jetpack MVVM 脚手架》项目不仅是我一人创作,也是诸多优秀开发者 “实事求是、互动演化” 结果。
以下项目架构一览图,主要包含 表现层、领域层、数据层 三层:

Activity/Fragment、DataBinding、Navigation,State-Holder 等组件位于表现层,专职 “UI 状态托管” 和 “UI 逻辑处理”;
Dispatcher、UseCase 等组件位于领域层,夹在表现层和数据层 中间,专职 “业务逻辑处理” 和 “Result 回推”;
DataRepository 和 本地、远程数据源 则安排在数据层。
本文旨在介绍 现有 Jetpack 架构组件基础上,我们分别于 高频场景 下做了哪些改进 以更好达成 “快速、稳定、不出预期外错误开发”,
因而如对 DataBinding、ViewModel、LiveData 等架构组件 “本质” 尚不熟悉,请先透过《Jetpack MVVM 精讲》完成 “前置知识” 铺垫,本文不作累述。
福利 1:DataBinding 严格模式
正如《Jetpack MVVM 精讲》所述,我们在表现层使用 DataBinding 而非 “直接调用 View 实例”,是为 通过 “可观察数据” 间接通知 View 刷新,来规避潜在的 “View 实例为 Null” 的隐患。也即 DataBinding 本质是 解决 View 实例 Null 安全一致性问题。
然而与基于 “函数式编程思想” Jetpack Compose 区别在于,DataBinding 并非通过 “纯函数方式” 隔绝手写代码对 “View 实例” 接触,而是透过 “自动化代码生成” 方式为 View 实例做 ”判空处理“,
而这也就带来一个问题 —— 你可在代码中透过 mBinding 实例来调用 View 实例 —— 如此等于舍本逐末、前功尽弃。
👆 👆 👆 划重点
因而基于对 “解决 View 实例 Null 安全一致性问题” 独家理解,“DataBinding 严格模式” 应运而生,通过它,可使 View 实例 Null 安全一致性问题 被彻底解决、安全性与 Jetpack Compose 持平。
不少小伙伴告诉我,他们已于实际开发中使用。

考虑到 “按需选用” 原则,现已抽取为 “依赖库” 独立维护。
GitHub:Strict-DataBinding
温馨提示:
使用 “DataBinding 严格模式” 后,于 “属性动画” 等 “对 View 实例强依赖” 场景,可借助 “Motion 动画” 等新式框架代替(Motion 动画学习成本不足属性动画 20%,且效果好、收益高,具体视频教程可见我们在《MotionChallenge》分享)。
如对 Jetpack Compose 基于函数式编程思想 “解决 View 实例 Null 安全一致性问题” 理论基础感兴趣,可详见《一通百通 “声明式 UI” 扫盲干货》拆解,此处不做累述。
Note 2023.5.13 :
福利 2:Dispatcher 回推一次性消息
根据《Jetpack MVVM 分层设计解析》篇 的分析易得,Jetpack 业务架构组件中,缺乏合适的 “一次性事件分发” 组件,
在 Kotlin 下我们可通过 SharedFlow 实现一次性事件的分发; Java 下尚且没有,
综合 “生命周期安全、作用域可控” 等需求,再加上 Jetpack LiveData 的设计存在缺陷,不适合承担 BehaviorSubject,笔者于 2019 年将 LiveData 往 PublishSubject 方向改造,去除其粘性设计,用于一次性事件的分发,
但 2021 年不断有消息流出,表示 LiveData 在 “事件分发” 上也处理的不好,例如 postValue 有丢事件几率,为此笔者在 2022 年封装和开源 Dispatcher 组件,其内通过消息队列设计,确保连续快速推送多个事件也不丢失,
注:在 “响应式编程” 中,BehaviorSubject 是 “用于响应 State 状态的组件”,PublishSubject 是 “用于响应一次性事件 Event 的组件”,
BehaviorSubject 的 Observer 回调不宜暴露给开发者,不然易造成《Jetpack MVVM 分层设计解析》篇 所述的两大隐患,
目前 Jetpack 架构组件中,仅 DataBinding ObservableField 能做到 “Observer 被屏蔽” 乃至完美承担 BehaviorSubject,为此 LiveData/StateFlow 等组件,注意谨慎使用,使用时务必遵循《“响应式框架” 使用避坑》篇 介绍的 “正确打开方式”。

考虑到 “按需选用” 原则,现已抽取为 “依赖库” 独立维护:
Github:MVI-Dispatcher
Kotlin 项目参见:
Github:MVI-Dispatcher-KTX
福利 3:Smooth-Navigation 使转场顺滑
早在 2019 年11 月,结合小伙伴反馈,我们于《Jetpack Navigation》篇文末加餐独家拆解了 “Navigation 被设计为 replace 转场的缘由”,并给出该背景下最优解。
如仍坚持使用 Fragment 作为页面,且对 “返场通知时机” 等因素控制无要求,那么将 replace 改为 add、hide 不失为简便选择。
得益于小伙伴反馈,我们发现 GitHub 上现有 “Navigation Add Hide 修改版” 皆存在致命缺陷 —— 例如在通过 “popUpToInclusive 越级返回时,未正确计数和出栈 Fragment,导致加载了预期外 Fragment” 等现象。
基于此,我们调试并修复这方面漏洞,作为 Smooth-Navigation 依赖库分享给大家。

考虑到 “按需选用” 原则,现已抽取为 “依赖库” 独立维护。
GitHub:Smooth-Navigation
Note 2020.10.28:
细节 A:通过 State-Holder 托管和恢复状态
关于 ViewModel 本质,早前我们在《ViewModel》篇 介绍道:
1.让状态管理独立于页面,从而实现 “状态管理分治”、“状态管理一致性” 和 “状态共享”,
2.为状态设置作用域,使状态共享做到作用域可控。
3.实现单向依赖,避免内存泄漏。
为此,基于单一职责原则,我们提取 “Jetpack ViewModel 的部分功能” 作为 State-Holder,使其专职于 “页面重建时状态的恢复”,
也即 State-Holder 在使用时,其作用域被限定为当前页面,且其内只包含 ObservableField 及它们的初始化操作,此外不包含任何逻辑。
在脚手架项目中,我们为每个页面都单独配备一个 State-Holder,专职页面状态的托管。
Note 2023.5.10:
2023 年起,官方也做了同样的事,甚至名字也和笔者取的一模一样 —— StateHolder,专用于 Jetpack Compose 界面状态的托管。

Note 2023.5.30: