前言
很高兴见到你!
最近在后台时有收到读者的留言,说能不能出一期 Jetpack MVVM 精讲,以及配套一份简练的案例,好把玩把玩、感受感受、加深对 MVVM 的印象。
答案当然是肯定的。
面向标准化开发已成现实
金九银十,相信有不少读者在抓紧机会面试。
Android 市场已今非昔比。在过去,迫于招人的压力,应试者只需了解四大组件、视图、网络请求,即可谋得一份满意的工作。
现如今,Jetpack 架构组件 及 标准化开发模式 的确立,意味着 Android 开发已步入成熟阶段:
许多 样板代码 不再需要开发者手写,而是可以通过模版工具 自动生成,在取缔繁杂耗时的重复工作的同时,避免因人工操作的疏忽,而造成难以排查、不可预期的错误。
这十分符合企业的利益,因而面试官在招人的时候,也更加看重应试者对 架构组件 —— 至少是 MVVM 的理解程度。
像“解耦”等 含糊其辞的说法,已经不能够被面试官所认可,稍微对 MVVM 有一点经验的面试官都会请你举例说明,好证明你确实对 MVVM 有着正确、深入的理解,能够自然而然地写出标准化、规范化的代码,能够迅速适应 各家公司自制的 自动化模版工具。
本文的目标
本人拥有 3 年的 移动端架构 践行和设计经验,领导团队重构的中大型项目多达十数个,对 Jetpack MVVM 架构在确立规范化、标准化 开发模式 以减少不可预期的错误 所作的努力,有着深入的理解。
因而本文的目标,就是结合 LifeCycler、LiveData、ViewModel、DataBinding,来融汇贯通地演绎一下:
作为 应用开发骨架 的 标准化状态管理框架,究竟为 快速开发过程中 减少不可预期的错误 做了哪些努力。
不同于 东拼西凑、人云亦云、徒添困扰 的网文,愿意将 标准化开发模式的 深度思考知识 和 实战反思经验 无保留地分享,全网仅此一家。这样的文章可以说是 看一篇、少一篇,因此,就算不去 hold 住面试官,也请务必跟随本文的脚步,无障碍地将 Jetpack MVVM 过一遍!
文章目录一览
- 前言
- 面向标准化开发已成现实
- 本文的目标
- Jetpack LifeCycler
- LifeCycler 存在前的混沌世界
- LifeCycler 为什么能解决上述这些问题?
- Jetpack LiveData
- LiveData 存在前的混沌世界
- LiveData 为什么能解决上述这些问题?
- LiveData 有个坑需要注意
- Jetpack ViewModel
- ViewModel 存在前的混沌世界
- ViewModel 为什么能做到这几点?
- Jetpack DataBinding
- DataBinding 存在前的混沌世界
- DataBinding 就是来解决这些问题
- 综上
Jetpack LifeCycler
LifeCycler 的存在,主要是为了解决 生命周期管理 的一致性问题
LifeCycler 存在前的混沌世界
在 LifeCycler 面市前,生命周期管理 纯靠手工维持,这样就容易滋生大量的一致性问题。
例如跨页面共享的 GpsManager 组件,在每个依赖它的 Activity 的 onResume 和 onPause 中都需要 手工 激活、解绑 和 叫停。
那么 随着 Activity 的增多,这种手工操作 埋下的一致性隐患 就会指数级增长:
一方面,凡是手工维持的,开发者容易疏忽,特别是工作交接给其他同事时,同事并不能及时注意到这些细节。
另一方面,分散的代码不利于修改,日后除了激活、叫停,若有其他操作需要补充(例如状态监听),那么每个 Activity 都需要额外书写一遍。
LifeCycler 为什么能解决上述这些问题?
LifeCycler 通过 模板方法模式 和 观察者模式,将生命周期管理的复杂操作,全部在作为 LifeCyclerOwner 的基类中(例如视图控制器的基类)封装好,默默地在背后为开发者运筹帷幄,
开发者因而得以在视图控制器(子类)中只需一句 getLifecycle().addObserver(GpsManager.getInstance) ,优雅地完成 第三方组件在自己内部 对 LifeCyclerOwner 生命周期的感知。
除了解决一致性问题,这样做还 顺带地提供了其他 2 个好处:
1.规避 为监听状态 而 注入视图控制器 的做法
当需要监听状态时,以往我们的做法是 通过方法手工注入 Activity 等参数,这埋下了内存泄漏的隐患 —— 因为团队中的新手容易因这是个 Activity,而在日后误将其依赖给组件中的其他成员。
现如今,我们可以直接在组件内部 点到为止 地监听 LifecycleOwner 的状态,从而规避这种不恰当的使用。
2.规避 为追溯事故来源 而 注入视图控制器 的做法
当发生事故时,以往我们若想在组件中 追溯事故来源,同样不得不从方法中直接注入 Activity 等,这同样埋下了内存泄漏的隐患。现如今组件因实现了 DefaultLifecycleObserver,而得以通过生命周期回调方法中的 LifecycleOwner 参数,在方法作用域中 即可得知事故来源,无需更多带有隐患的操作。
如果这么说还不理解的话,可具体参考 《为你还原一个真实的 Jetpack LifeCycler》 中提供的 GpsManager 案例,本文不再累述。
Jetpack LiveData
LiveData 的存在,主要是为了帮助 新手老手 都能不假思索地遵循 通过唯一可信源分发状态 的标准化开发理念,从而使在快速开发过程中 难以追溯、难以排查、不可预期 的问题所发生的概率降低到最小。
LiveData 存在前的混沌世界
在 LiveData 面市前,我们分发状态,多是通过 EventBus 或 Java Interface 来完成的。不管你是用于网络请求回调的情况,还是跨页面通信的情况。
那这造成了什么问题呢?首先,EventBus 只是纯粹的 Bus,它 缺乏上述提到的 标准化开发理念 的约束,那么人们在使用这个框架时,容易因 去中心化 地滥用,而造成 诸如 毫无防备地收到 预期外的 不明来源的推送、拿到过时的数据 及 事件源追溯复杂度 为 n² 的局面。
并且,EventBus 本身缺乏 LifeCycler 的加持,存在生命周期管理的一致性问题。这是 EventBus 的硬伤,也是我拒绝使用 EventBus 的最主要因素。
对上述状况不理解的,可具体参考《LiveData 鲜为人知的 身世背景 和 独特使命》 中提供的 播放器状态全局通知 的案例
LiveData 为什么能解决上述这些问题?
首先,LiveData 是在 Google 希望确立 标准化、规范化 的开发模式 —— 这样一种背景下诞生的,因而为了达成这个艰巨的 使命,Google 十分克制地将其设计为,仅支持状态的输入和监听,从而,它不得不 在单例的配合下,承上启下地完成 状态 从 唯一可信源 到 视图控制器 的输送。
(ViewModel 姑且也算是一种单例,一种工厂模式实现的伪单例。唯一可信源是指 生命周期独立于 视图控制器的 数据组件,通常是 单例 或共享 ViewModel)
这使得任何一次状态推送,都可预期、都能方便地追溯来源,而不至于在 事件追溯复杂度为 n² 的迷宫中白费时间。(即,无论是从哪个视图控制器发起的 对某个共享状态改变的请求,状态最终的改变 都由 作为唯一可信源的 单例或 SharedViewModel 来一对多地通知改变)
并且,这种承上启下的方式,使得单向依赖成为可能:单例无需通过 Java Interface 回调通知视图控制器,从而规避了视图控制器 被生命周期更长的单例 依赖 所埋下的内存泄漏的隐患。
LiveData 有个坑需要注意
不过,LiveData 的设计有个坑,这里我顺带提一下。
为了在视图控制器发生重建后,能够 自动灌倒 所观察的 LiveData 的最后一次数据,LiveData 被设计为粘性事件。
—— 我姑且认为这和 Jetpack Navigation 只支持 replace 方式切换 Fragment 一样,是个拓展性不佳的设计,甚至可以说是一个 bug,
因为 MVVM 是一个整体,既然 ViewModel 支持共享作用域,并且官方文档都承认了通过 共享 ViewModel 来实现跨页面通信的需求,
那么基于 “开闭原则”,LiveData 理应提供一个与 MutableLiveData 平级的底层支持,专门用于非粘性的事件通信的情况,否则直接在跨页面通信中使用 MutableLiveData 必造成 事件回调的一致性问题 及 难以预期的错误。
关于非粘性 LiveData 的实现,网上存在通过 “事件包装类”(只适合 kotlin 的情况) 和 “反射干预 LastVersion” (适用于 Java 的情况)两种方式来解决:
https://juejin.im/post/5b2b1b2cf265da5952314b63
https://blog.csdn.net/geyuecang/article/details/89028283
无论是使用哪一种实现,我都建议 遵循传统 LiveData 所遵循的开发理念,通过唯一可信源分发状态,来方便事件源头的追溯。对于 “去中心化” 的 Bus 方式,我拒绝在项目中这样使用。
(具体我会在未来开源的最佳实践项目中 展示 UnPeekLiveData 的使用)
Jetpack ViewModel
ViewModel 的存在,主要是为了解决 状态管理 和 状态共享 的问题。
ViewModel 存在前的混沌世界
ViewModel 的本职工作是 状态托管 和 状态管理的分治,也即当视图控制器重建时,
对于轻量的状态,可以通过视图控制器基类的 saveInstanceState 机制,以序列化的方式完成存储和恢复。
对于重量级的状态,例如通过网络请求得到的 List,可以通过生命周期长于视图控制器的 ViewModel 持有,从而得以直接从 ViewModel 恢复,而不是以效率较低的序列化方式。
在 Jetpack ViewModel 面市之前,MVP 的 Presenter 和 MVVM - Clean 的 ViewModel 都不具备状态管理分治的能力。
Presenter 和 Clean ViewModel 的生命周期都与视图控制器同生共死,因而它们顶多是为 DataBinding 提供状态的托管,而无法实现状态的分治。
到了 Jetpack 这一版,ViewModel 以精妙的设计,达成了状态管理,以及可共享的作用域。
ViewModel 为什么能做到这几点?
其实这版主要是基于 工厂模式,使得 ViewModel 被 LifeCyclerOwner 所持有、通过 ViewModelProvider 来引用,
所以 它既类似于单例: —— 当被作为 LifeCyclerOwner 的 Activity 持有时,能够脱离 Activity 旗下 Fragment 的生命周期,从而实现作用域共享,
实际上又不是单例: —— 生命周期跟随 作为 LifeCyclerOwner 的视图控制器,当 Owner(Activity 或 Fragment)被销毁时,它也被 clear。
此外,出于对视图控制器重建的考虑,Google 在视图控制器基类中通过 retain 机制对 ViewModel 进行了保留。
因此,对于 作用域共享 和 视图重建 的情况,状态因完好地被保留,而得以被视图控制器在恢复时直接使用。
再者,由于存在 共享作用域的考虑,所以 ViewModel 本身也承担了跨页面通信(例如事件回调)的职责。前面在介绍 LiveData 时,对于 LiveData 在事件通信时粘性设计的问题已经介绍过了,这里不再累述。
Jetpack DataBinding
DataBinding 的存在,主要是为了解决 视图 的一致性问题。
DataBinding 存在前的混沌世界
在 DataBinding 面市前,我们若要改变视图的状态,首先就要引用该视图,例如 textView.setText(),
这造成什么问题呢?
当页面存在横、竖布局,且两种布局的控件存在差异,例如横屏存在 textView 控件,而竖屏没有,那么我们就不得不在视图控制器中为 textView 做判空处理,这就造成了一致性问题 —— 容易疏忽而忘记判空,毕竟页面多达数十个、每个页面的控件也无数。
那怎么办呢?
DataBinding 就是来解决这些问题
通过在布局中与可观察的数据发生绑定,那么当该数据被 set 新的内容时,控件也将得到通知和刷新。
换言之,在使用 DataBinding 后,唯一的改变是,你无需手工调用视图来 set 新状态,你只需 set 数据本身。
因而,DataBinding 并非许多人不假思索认为的,将 UI 逻辑搬到 XML 中写 从而难以调试 —— 事实根本不是这样的:
DataBinding 只负责绑定数据、负责作为 UI 逻辑末端的状态的改变(也即它是一个不可再分的原子操作,本来就不需要调试),原本在视图控制器中 UI 逻辑怎么写,现在还是怎么写,只不过不再需要 textView.setText(xxx),而是直接 xxx.set()。
所以在 DataBinding 的帮助下,好处总共有多少个呢?
1.规避了视图状态的 一致性问题 —— 无需手工判空。
2.规避了视图状态的 一致性问题,乃至无需视图调用,从而完全不用编写 findViewById。
3.就算要调用视图,也不用 findViewById,而是直接通过 binding 来引用。
4.先前的 UI 逻辑基本不用改动,改的只是作为末端的状态改变的方式。
……
此外,DataBinding 有个大杀器就是,能为控件提供自定义属性的 BindingAdapter,它不仅可以解决 圆角 Drawable 复用的问题(你懂得),还可以实现 imageView 直接绑定 url 等需求,总之,没有它办不到的,只有你想不到的,DataBinding 的好处等着你挖掘。
关于 DataBinding 的注意事项,以及屡试不爽的排坑技巧,可具体参考 《从 被反对 到 真香 的 Jetpack DataBinding!》,这里不做累述。
综上
LifeCycler 的存在,主要是为了解决 生命周期管理 的一致性问题。
LiveData 的存在,主要是为了帮助 新手老手 都能不假思索地 遵循 通过唯一可信源分发状态 的标准化开发理念,从而在快速开发过程中 规避一系列 难以追溯、难以排查、不可预期 的问题。
ViewModel 的存在,主要是为了解决 状态管理 和 状态共享 的问题。
DataBinding 的存在,主要是为了解决 视图 的一致性问题。
它们的存在 大都是为了解决一致性的问题、将容易出错的操作在后台封装好,方便使用者快速、稳定、不产生预期外错误地编码。
这样说,你理解了吗?
结语:Jetpack MVVM 最佳实践的案例,计划于 2019 年 10 月底 在 GitHub 发布。届时我会以各种形式留下访问方式,敬请期待!
本文来源:KunMinX
最后
感谢大家能耐着性子,看完我啰哩啰嗦的文章。
我愿与各位坚守在Android开发岗位的同胞们互相交流学习,共同进步!
在这里我也分享一份自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
如果你有需要的话,可以顶一下
相关推荐
Demo采用Retrofit+RxJava+Okhttp网络请求,演示了JetPack中Navigation、LifeCycle+ViewModel+LiveData+Room+DataBinding+MVVM开发使用示例。
本项目是基于Java的Jetpack MVVM最佳实践...这个项目是Jetpack MVVM的最佳实践,旨在帮助开发者更好地理解和掌握Jetpack MVVM的开发模式,通过简洁而高效的代码,形成对视图控制器乃至标准化开发模式的正确和深入理解。
在Android开发领域,Jetpack MVVM架构组件是目前推荐的最佳实践之一,它为开发者提供了更加清晰、可测试和模块化的代码结构。本实践案例将深入探讨如何有效地利用Jetpack MVVM来构建高效、稳定的Android应用。 MVVM...
为了满足广大Android开发爱好者与从业者的学习需求,我们精心整理并上传了一份全面而实用的Android项目资源包。这份资源包内容丰富,涵盖了从基础知识到实战应用的全方位内容,旨在为开发者们提供一个便捷、高效的...
该项目提供了一整套基于Java和HTML的Jetpack MVVM架构设计源码,共计137个文件,其中包括71个Java源文件、36个...源码深入实践了Jetpack MVVM的最佳模式,旨在帮助开发者对视图控制器和标准化开发模式有更深刻的理解。
本源码为基于Kotlin的Jetpack MVVM天气预报APP设计,包含23个PNG文件、18个Kotlin文件等,共74个文件。该项目旨在为用户提供一个全面、便捷的天气预报解决方案,通过Kotlin和MVVM技术的结合,为用户带来高效的使用...
本项目为基于Jetpack MVVM架构的Java+HTML实战最佳实践设计源码,包含141个文件,涵盖67个Java源文件、37个XML配置文件、20个PNG图片文件、4个Gradle构建文件、3个Git忽略规则文件,并附有必要的HTML和MD文档。...
总之,KingWeather项目为开发者提供了一个实际的示例,展示了如何利用Jetpack MVVM架构组件来构建一个专业且可扩展的Android应用。通过研究该项目的源代码,开发者可以学习到如何优雅地组织代码、处理数据流以及实现...
在Android开发领域,MVVM(Model-View-ViewModel)架构结合Jetpack库的使用已经成为现代应用设计的标准。本文将深入探讨MVVM架构的核心概念、它如何与Jetpack组件结合,以及如何利用Kotlin、RxJava等相关技术提升...
之前的README文件(在这里可以看到 )和现有代码实际上已经有引入出入,也包含了一些不正确的观点,回来更新一下。 翻一下提交记录,第一次提交是在...在Android开发中,到底什么是MVVM架构? 数据驱动UI? ViewModel
在本讲中,我们探讨的是如何利用Jetpack框架来构建一个遵循MVVM架构的Android应用。Jetpack是一个由Google提供的工具集,旨在简化开发过程,提升代码质量,确保应用在不同Android版本和设备上表现一致。它包含多个库...
一 快速入门kotlin(上) 1.变量和函数 2.程序逻辑控制 3.面相对象编程 4.Lambda编程 5.空指针检查 6.内嵌表达式,参数默认值 二 快速入门kotlin(中) 7.标准函数和静态函数 ...八 搭建MVVM项目架构(下)
除此之外,随着技术的发展,Android开发还包括了对新特性和API的学习,例如Material Design设计规范、Jetpack组件库、Room持久化库、LiveData和ViewModel等MVVM架构组件,以及Android Jetpack Compose用于构建现代...
综上所述,jetpack_mvvm项目通过结合Jetpack组件和Kotlin的优势,为Android应用开发提供了一个高效、模块化的MVVM架构实现,便于开发者构建稳定、可测试的应用程序。通过研究这个项目,开发者可以深入理解如何利用...
7. **Jetpack组件**:Google推出的Jetpack库集成了许多Android开发的最佳实践,包括ViewModel、LiveData、Room等,为MVVM架构提供了强大的支持。 在"SumTea_Android-master"这个项目中,你可以期待看到这些概念的...
总的来说,AndroidBaseFrameMVVM是一个全面集成现代Android开发技术的框架,它通过MVVM架构、Jetpack组件和Kotlin语言,帮助开发者快速构建稳定、高效且易于维护的应用程序。同时,它还引入了ARouter和MMKV,进一步...
【标题】中的“多线程精品资源-- Android学习知识点总结 Jetpack、MVVM、MVI、Kotlin、ViewPager”揭示了这个压缩包包含了一系列关于Android开发的重要知识点,具体包括: 1. **Jetpack**:这是Google推出的一套...
在Android应用开发中,Jetpack已经成为现代Android架构的重要组成部分,它提供了一系列的库和工具,帮助开发者构建稳定、可维护的代码。本项目是基于Jetpack实现的一个MVVM(Model-View-ViewModel)架构的天气应用...
通过研究这些代码,开发者可以更好地理解如何在实际项目中应用Jetpack组件和MVVM架构,以及如何实现组件化的Android应用。 总的来说,这个资源为Android开发者提供了一个学习和实践现代开发技术的平台,特别是对于...
在Android中,MVVM通过DataBinding库得以实现,该库提供了双向数据绑定功能,使得视图(View)和模型(ViewModel)之间可以直接通信,而无需手动设置监听器。 1. **DataBinding库的引入**:首先,你需要在项目的...