869e38c721248515a899c6dcf53014c9
重学安卓:这是一份 “架构模式” 的自驾攻略

往期回顾专栏目录更新动态优惠政策版权须知

温馨提示:如果这是第一次接触《重学安卓》,可通过上述链接来访问和快速了解《重学安卓》专栏、获取它的目录、试读内容,以及了解它的最新动态 和 发展状况。

截至目前,专栏已对 体系化文章 做了 1970 余次修订,数十位群友告诉我 受专栏的启发 他们也开启了写作之路。群里不定期会有小伙伴讨论适配问题、分享原创的开源库 和 提供内推机会,订阅后可随时进群交流。

·

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:UnPeek-LiveData 发送一次性事件
  • 福利 3:Smooth-Navigation 使转场顺滑
  • Note 2020.10.28 加餐
    • 透过 State-ViewModel 托管和恢复状态
    • 透过 Event-ViewModel 跨页面通信和 “消息鉴权”
  • 通过 Request 来复用业务逻辑
  • Note 2020.11.03 加餐
    • 正确区分 “可变 State” 和 “只读 Event” 的本质及作用
    • 而这也就顺带解析了 为什么有了 ViewModel 还要有 Request
  • 通过 UseCase 管理可叫停的业务
  • 通过 DataResult 回调数据层结果
  • Note 2020.12.01 加餐
    • 对 DataResult v1.1 新设计的补充说明
  • 综上

架构图总览

《Jetpack MVVM 脚手架》项目不仅仅是我一个人的创作,也是诸多优秀开发者 实事求是、互动演化的结果。

以下是项目架构一览图,主要包含 表现层、领域层、数据层 三层:

Activity/Fragment、DataBinding、Navigation,State-ViewModel 等组件位于表现层,专职 “UI 状态的托管” 和 “UI 逻辑的处理”;

Event-ViewModel、Request、UseCase 等组件位于领域层,夹在 表现层和数据层 中间,专职 “业务逻辑的处理” 和 “Event 的分发”;

DataRepository 和 本地、远程数据源 则安排在数据层。

本文旨在介绍 在现有架构组件的基础上,我们分别在 高频场景 下做了哪些改进 以更好地达成 “快速、稳定、不出预期外错误地开发”,

因而对于 DataBinding、ViewModel、LiveData 等架构组件的 “本质” 尚不熟悉的朋友,请先透过《Jetpack MVVM 精讲》获取前置知识的铺垫,本文不作累述。

福利 1:DataBinding 严格模式

正如《Jetpack MVVM 精讲》中提到的,我们在表现层使用 DataBinding 而不是 “直接调用视图实例”,是为了 通过 “可观察数据” 间接通知视图刷新,来规避可能存在的 视图实例为 null 的安全隐患。也即 DataBinding 的本质是 解决视图调用的一致性问题

然而与基于 “函数式编程思想” 的 Jetpack Compose 不同的是,DataBinding 并非是通过纯函数的方式来隔绝手写代码对 “视图实例” 的接触,而是透过 “自动化代码生成” 的方式来为视图实例做 ”判空处理“

而这也就带来了一个问题 —— 你可以在代码中透过 mBinding 实例来调用视图实例 —— 如此等于舍本逐末、前功尽弃。

👆 👆 👆 划重点

因而基于对 “解决视图调用一致性问题” 的独家理解,“DataBinding 严格模式” 应运而生,通过它,可使视图调用一致性问题 被彻底 (100%) 解决、安全性与 Jetpack Compose 持平。

有不少小伙伴告诉我,他们已在实际开发中使用。

考虑到 “按需” 选用的原则,现已抽取为 “依赖库” 独立维护。

GitHub:Strict-DataBinding

温馨提示:

在使用 “DataBinding 严格模式” 后,对于 “属性动画” 等 “对视图实例强依赖” 的场景,可借助 “Motion 动画” 等新式框架代替(Motion 动画的学习成本不足属性动画的 20%,且效果好、收益高,具体视频教程可见我们在《MotionChallenge》的分享)。

如对 Jetpack Compose 基于函数式编程思想 “解决视图调用一致性问题” 的理论基础感兴趣,可详见《一通百通 “声明式 UI” 扫盲干货》的铺垫,此处不做累述。

福利 2:UnPeek-LiveData 发送一次性事件

众所周知,LiveData 被设计为用于接收数据,并 配合唯一可信源、以 “生命周期安全” 的方式去给订阅者页面分发 “可靠一致的消息”

与此同时,“页面返场回调” 是个极高频的场景,出于对 “生命周期安全、作用域的限制、事件源的可追溯” 等背景因素 的收集和分析,我们否决了 EventBus、LiveDataBus、Result API 在页面回调场景的使用,取而代之的是透过 LiveData + SharedViewModel 的组合来作为当下的 最优解,因而问题也就进一步转化为 “解决 LiveData 自身的粘性” 等问题(也即 解决返场场景下 “数据倒灌” 等问题)。

如对 “数据倒灌场景的复现”、

“为何 LiveData + SharedViewModel 是最优解”、

及 “为何放弃 EventBus、LiveDataBus、Result API” 感兴趣,

详见《LiveData 数据倒灌 背景缘由全貌 独家解析》的铺垫,此处不做累述。

托福于小伙伴们对它的宠爱有加,过去一年陆陆续续收到 10 位小伙伴 30 多次 bug 反馈,以及 5 位小伙伴自发分享的源码设计思路,使框架得以在一次次交流互动和灵感萌生下 演化为现在的样子,

截至目前,UnPeekLiveData 实现和保留的特点如下:

1.一条消息能被多个观察者消费(since v1.0)

2.消息被所有观察者消费完毕后才开始阻止倒灌(since v4.0)

3.可以通过 clear 方法手动将消息从内存中移除(since v4.0)

4.让非入侵设计成为可能,遵循开闭原则(since v3.0)

并且我们仍保留了 构造器模式 的设计,后续如有定制功能拓展,可逐步在 Builder 中添加配置。

考虑到 “按需” 选用的原则,现已抽取为 “依赖库” 独立维护。

GitHub:UnPeek-LiveData

福利 3:Smooth-Navigation 使转场顺滑

早在去年 8 月份,结合小伙伴的反馈,我们在《Jetpack Navigation》篇文末加餐中独家解析了 “Navigation 被设计为 replace 转场的缘由所在”,并给出了该背景下的最优解。

如果仍坚持使用 Fragment 作为页面,且对 “返场通知时机” 等因素的控制没有要求的话,那么将 replace 改为 add、hide 不失为简便的选择。

然而得益于小伙伴的反馈,我们发现了 GitHub 上现有的 “Navigation Add Hide 修改版” 其实都存在致命的弊病 —— 例如在通过 “popUpToInclusive 越级返回时,未正确计数和出栈 Fragment,导致加载了预期外的 Fragment” 的现象

基于此,我们调试并修复了这方面的漏洞,作为 Smooth-Navigation 依赖库分享给大家。

考虑到 “按需” 选用的原则,现已抽取为 “依赖库” 独立维护。

GitHub:Smooth-Navigation

Note 2020.10.28 加餐

1.透过 State-ViewModel 托管和恢复状态

关于 ViewModel 的本质,早前我们在《ViewModel》篇 介绍道:

1.透过状态管理的分治 来减少页面重建时不必要的资源开销

2.透过工厂模式 实现状态作用域的可控

3.配合 LiveData 实现单向依赖,以规避内存泄漏的风险

而 State-ViewModel 则是关于 Jetpack ViewModel 最基本的使用:

在脚手架的项目中,我们为每个页面都单独配备一个 State-ViewModel,职责仅限于状态托管和恢复,也即 State-ViewModel 中主要包含 ObservableField、LiveData、Request 以及它们的初始化操作,除此之外不包含任何逻辑

top Created with Sketch.