简介
做了几年开发后,在 Android 领域我一直期望找到一种较为合理且易用的开发模式。
从 MVC 到 MVP,再到现在的 MVVM,配合 Jetpack 套件,以及 Kotlin 和协程,还有 Rx 系列,我认为 Android 的开发范式已经成熟了,再通过 AS 和 Gradle 提供的动态引入代码的功能,完全可以封装一套开发框架,并尽可能提供定制化,基于基本的开发范式,通过配置文件来决定框架的内容。
在开始之前,先来看一张 Google 推荐的架构图:
这是一张 MVVM 架构的示意图,分为 View、ViewModel、Model,三个层次,层与层之间有明确的调用界限,应该尽可能的避免跨层调用,但是代码的灵活性是很高的,你很难完全避免团队成员开发不符合框架标准的代码,一旦有成员对框架的理解不够全面,那么他极可能在交付时间的压力下,选择他熟悉的实现方式来开发。
举一个例子就是:你期望他使用 Glide 来加载图片,可是因为他之前一直用 Fresco,搞不好你稍不留神他就给你来个意外,自行引入了 Fresco,但其实学习 Glide 成本是很低的,尤其是在经过封装的情况下。
但是人就是这样,总是倾向于自己熟悉的。
所以在团队中,应该对成员进行一定的培训,以及督促成员自行学习和领悟框架中的知识,并积极发现和上报问题,持续改进框架,而不是一言不合的就想重构推倒整个架构,不得不说“重构”也是新人经常提的问题,并不是说一定不能说重构,但说实在很多时候都是因为他对框架不理解,就抨击框架,想法设法想改成自己熟悉的,这个时候团队领导就得正确引导了。
综上所述,维护一套框架的成本是蛮大的,需要形成相应的文档,讲解视频,并组织培训等,但好处也是显而易见的,统一的思想和框架标准,让每一个成员都成为了备胎,只要有需要,都可以快速上手,专注于业务逻辑,提高成员的可替代性,不会因为一人的离职而造成他负责的业务无人可接的情况。
好了,下面我们看看如何来创建这样的一个基本框架。
期望达到的目标
我们先来定目标,理清楚我们对于框架的期望。经过梳理,我认为框架要达到的目标如下:
1. 基础的框架范式
采用 MVVM 模式。框架会对基本的功能进行封装,使得层与层之间的调用变得容易,减少样板代码,易于上手,对于必不可少的样板代码,可以通过 AS 的 File Templates 和 Live Templates 来快速生成,或者写个 AS 插件就更好了。
2. 统一的第三方库
对于库的使用尽可能进行封装,避免引入功能重复的库。尽量使用官方的。
3. 尽量可定制化,减少包大小
对于某些小型的项目,或者项目中不想要的第三方库,可以通过配置文件去除指定的库,减少包的大小。甚至对于部分功能,也要做到可定制化,尽量避免不必要的实例创建。
4. 统一的框架维护,更新便捷。
利用 git 的 submodule 功能,引入框架的源码,不采用远程仓库的方式,因为远程仓库每次更新都要更新版本,太麻烦了,不灵活。
如果你的代码是要交付出去的,比如外包,那么最终可以清除掉框架的 .git 目录,同时对框架进行清理后再交付,以免交付了客户不想要的代码,同时也对框架做一个保留。
团队成员齐心协力对框架的各项功能进行持续的优化,对于框架的更新和代码合并,需进行评审。
View 视图层,简称 V 层
在 Android 中,属于 View 层的有 Activity/Fragment,还有对应的 xml 文件。
关于布局的建议,优先采用 ConstraintLayout,但是如果很明显是线性布局的,采用 LinearLayout,简单列表采用 ListView,简单网格采用 GridView,复杂的用 RecyclerView。
下拉刷新上拉加载优先用 scwang90/SmartRefreshLayout,如果只有下拉刷新,采用 SwipeRefreshLayout。
视图层会封装 BaseActivity/BaseFragment,持有 ViewModel 和 ViewDataBinding 的实例,这样用户的操作可以流向 VM 层。
在 V 层发起异步,可使用 lifecycleScope 或 lifecycle.coroutineScope 协程,让异步跟随生命周期,在生命周期结束时取消协程。
ViewModel 业务逻辑层,简称 VM 层
在 Android 中,VM 层是由继承自 ViewModel 或 AndroidViewModel 的类组成,VM 层持有 Repository 仓库实例,可以向仓库请求数据,那么如何把数据流向 V 层呢?首先要明确的一个点就是:VM 层绝对不能持有 V 层的实例,这个是至关重要的,否则会造成内存泄露。
那么不持有 V 层实例如何把数据流过去呢?
答案是通过 LiveData 或 DataBinding 将数据流回 V 层。只有这两种做法。
另外,通过 Lifecycles,VM 可以 感知 V 层的生命周期,VM 层的生命周期和 V 层保持一致,这也是可以封装的点。但只有 V 完全销毁时,onCleared 方法才会回调。
在 VM 层发起异步或调用 Repository 数据,使用 viewModelScope 生命周期感知型协程替代 Rx,在界面销毁时可自动取消协程,性能也好。
或者使用 liveData 协程,这样在 liveData 被激活时自动执行协程,如果未执行完成就被取消,那么下次激活时还会自动执行。
VM 和 V 层的交互,很大程度上可以通过 DataBinding 来实现,DataBinding 是 Google 推出的数据绑定库,应该尽量使用。当然,不用的话,也可以在 V 层写相关的代码,但是 DataBinding 可以最大程度的减少样板代码,默认框架应该要求使用 DataBinding。
VM 层和 V 层可以封装一些常用的功能,比如加载中对话框,启动和结束界面等,同时应该考虑到有些界面不需要这些功能,因此也应该提供取消这些功能的接口,以免造成不必要的实例创建。
Model 数据层,简称 M 层
主要用 Repository 仓库来表示,Retrofit2 用于请求网络,Gson 用于解析 JSON,Room 用于存储本地数据。
访问网络、数据库、文件等,均可以使用协程来实现异步。
通常来说,Repository 会持有多个数据实例,比如网络数据实例和本地数据实例,但是对外暴露的接口,提供的数据来源对于使用者是无感知的,即 VM 或 V 层是不用关心数据的来源,统一由 M 层来控制。
在 Google 的架构指南中,推荐使用 Room 作为单一的数据源,即使用者获取数据全部通过 Room,而利用 Room 的特性,M 层操作 Room 将会自动响应给使用者最新的变化。
这种做法好不好,通常还得结合需求,在请求网络数据前,为了防止出现白屏,经常会先使用本地数据,等到网络数据请求成功了再刷新。
Jetpack 必须有的库
基础
AndroidX
AppCompat
MultiDex
Fragment
架构
DataBinding
Lifecycles
LiveData
ViewModel
Jetpack 可选的库
Benchmark
Security
Navigatio
Room
Paging
WorkManager
CameraX
DownloadManager
Notifications
Permissions
Preference
Animation
Palette
其他必须的库
ConstraintLayout
Kotlin
Kotlin-ktx
其他可选的库
RecyclerView
material
swiperefreshlayout
viewPager2
retrofit2、retrofit2RxJava2
glide
gson、gsonConverter
rxAndroid2、rxJava2、rxKotlin
debugDB
leakCanary2
bindingAdapter
livePermissions
各种 ktx
各种可选的支持库
GridLayout
CardView
…
总结
综上所述,我在 Github 中开源了 MVVMArchitecture 框架,灵感是来自 MVVMHabit。
MVVMArchitecture 主代码使用的是 Kotlin 编写,部分工具类代码使用的 Java,对于框架的内容,大幅度增加了定制化功能,尽量做到全局可配置和单独可配置,详见 Github 的说明,后续会进行持续的优化和改进,欢迎 fork、star、issue。
最后
Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。
千里之行始于足下,愿你我共勉。
这里整合了很多底层原理的知识,还有我认为比较重要的学习方向和知识点,放在了我的GitHub,欢迎大家一起学习进步。