作了幾年開發後,在 Android 領域我一直指望找到一種較爲合理且易用的開發模式,從 MVC 到 MVP,再到如今的 MVVM,配合 Jetpack 套件,以及 Kotlin 和協程,還有 Rx 系列,我認爲 Android 的開發範式已經成熟了,再經過 AS 和 Gradle 提供的動態引入代碼的功能,徹底能夠封裝一套開發框架,並儘量提供定製化,基於基本的開發範式,經過配置文件來決定框架的內容。git
在開始以前,先來看一張 Google 推薦的架構圖:github
這是一張 MVVM 架構的示意圖,分爲 View、ViewModel、Model,三個層次,層與層之間有明確的調用界限,應該儘量的避免跨層調用,可是代碼的靈活性是很高的,你很難徹底避免團隊成員開發不符合框架標準的代碼,一旦有成員對框架的理解不夠全面,那麼他很可能在交付時間的壓力下,選擇他熟悉的實現方式來開發。數據庫
舉一個例子就是:你指望他使用 Glide 來加載圖片,但是由於他以前一直用 Fresco,搞很差你稍不留神他就給你來個意外,自行引入了 Fresco,但其實學習 Glide 成本是很低的,尤爲是在通過封裝的狀況下。可是人就是這樣,老是傾向於本身熟悉的。網絡
因此在團隊中,應該對成員進行必定的培訓,以及督促成員自行學習和領悟框架中的知識,並積極發現和上報問題,持續改進框架,而不是一言不合的就想重構推倒整個架構,不得不說「重構」也是新人常常提的問題,並非說必定不能說重構,但說實在不少時候都是由於他對框架不理解,就抨擊框架,想法設法想改爲本身熟悉的,這個時候團隊領導就得正確引導了。架構
綜上所述,維護一套框架的成本是蠻大的,須要造成相應的文檔,講解視頻,並組織培訓等,但好處也是顯而易見的,統一的思想和框架標準,讓每個成員都成爲了備胎,只要有須要,均可以快速上手,專一於業務邏輯,提升成員的可替代性,不會由於一人的離職而形成他負責的業務無人可接的狀況。框架
好了,下面咱們看看如何來建立這樣的一個基本框架。異步
咱們先來定目標,理清楚咱們對於框架的指望。通過梳理,我認爲框架要達到的目標以下:ide
採用 MVVM 模式。框架會對基本的功能進行封裝,使得層與層之間的調用變得容易,減小樣板代碼,易於上手,對於必不可少的樣板代碼,能夠經過 AS 的 File Templates 和 Live Templates 來快速生成,或者寫個 AS 插件就更好了。工具
對於庫的使用盡量進行封裝,避免引入功能重複的庫。儘可能使用官方的。佈局
對於某些小型的項目,或者項目中不想要的第三方庫,能夠經過配置文件去除指定的庫,減小包的大小。甚至對於部分功能,也要作到可定製化,儘可能避免沒必要要的實例建立。
利用 git 的 submodule 功能,引入框架的源碼,不採用遠程倉庫的方式,由於遠程倉庫每次更新都要更新版本,太麻煩了,不靈活。 若是你的代碼是要交付出去的,好比外包,那麼最終能夠清除掉框架的 .git 目錄,同時對框架進行清理後再交付,以避免交付了客戶不想要的代碼,同時也對框架作一個保留。 團隊成員齊心合力對框架的各項功能進行持續的優化,對於框架的更新和代碼合併,需進行評審。
在 Android 中,屬於 View 層的有 Activity/Fragment,還有對應的 xml 文件。
關於佈局的建議,優先採用 ConstraintLayout,可是若是很明顯是線性佈局的,採用 LinearLayout,簡單列表採用 ListView,簡單網格採用 GridView,複雜的用 RecyclerView。
下拉刷新上拉加載優先用 scwang90/SmartRefreshLayout,若是隻有下拉刷新,採用 SwipeRefreshLayout。
視圖層會封裝 BaseActivity/BaseFragment,持有 ViewModel 和 ViewDataBinding 的實例,這樣用戶的操做能夠流向 VM 層。
在 V 層發起異步,可以使用 lifecycleScope 或 lifecycle.coroutineScope 協程,讓異步跟隨生命週期,在生命週期結束時取消協程。
在 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 層能夠封裝一些經常使用的功能,好比加載中對話框,啓動和結束界面等,同時應該考慮到有些界面不須要這些功能,所以也應該提供取消這些功能的接口,以避免形成沒必要要的實例建立。
主要用 Repository 倉庫來表示,Retrofit2 用於請求網絡,Gson 用於解析 JSON,Room 用於存儲本地數據。
訪問網絡、數據庫、文件等,都可以使用協程來實現異步。
一般來講,Repository 會持有多個數據實例,好比網絡數據實例和本地數據實例,可是對外暴露的接口,提供的數據來源對於使用者是無感知的,即 VM 或 V 層是不用關心數據的來源,統一由 M 層來控制。
在 Google 的架構指南中,推薦使用 Room 做爲單一的數據源,即便用者獲取數據所有經過 Room,而利用 Room 的特性,M 層操做 Room 將會自動響應給使用者最新的變化。
這種作法好很差,一般還得結合需求,在請求網絡數據前,爲了防止出現白屏,常常會先使用本地數據,等到網絡數據請求成功了再刷新。
Benchmark
Security
Navigation
Room
Paging
WorkManager
CameraX
DownloadManager
Notifications
Permissions
Preference
Animation
Palette
綜上所述,我在 Github 中開源了 MVVMArchitecture 框架,部分靈感是來自 MVVMHabit 和 MVVMLin。
MVVMArchitecture 主代碼使用的是 Kotlin 編寫,部分工具類代碼使用的 Java,對於框架的內容,大幅度增長了定製化功能,儘可能作到全局可配置和單獨可配置,詳見 Github 的 Wiki,後續會進行持續的優化和改進,歡迎 fork、star、issue。