您好,歡迎來到網暖!
?
當前位置:網暖 » 站長資訊 » 建站基礎 » 網絡技術 » 文章詳細 訂閱RssFeed

令Android開發者耳目一新的 Jetpack MVVM 精講!

來源:網絡整理 瀏覽:284次 時間:2020-02-17
前言

很高興見到你!

最近在后臺時有收到讀者的留言,說能不能出一期 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 都需要額外書寫一遍。

cac831b78a1142038760966746049d93


LifeCycler 為什么能解決上述這些問題?

LifeCycler 通過 模板方法模式 和 觀察者模式,將生命周期管理的復雜操作,全部在作為 LifeCyclerOwner 的基類中(例如視圖控制器的基類)封裝好,默默地在背后為開發者運籌帷幄,

開發者因而得以在視圖控制器(子類)中只需一句 getLifecycle().addObserver(GpsManager.getInstance) ,優雅地完成 第三方組件在自己內部 對 LifeCyclerOwner 生命周期的感知。

ca519b3907f44d50af4d2f625f6f928f


除了解決一致性問題,這樣做還?順帶地提供了其他 2 個好處:

1.規避 為監聽狀態 而 注入視圖控制器 的做法

當需要監聽狀態時,以往我們的做法是 通過方法手工注入 Activity 等參數,這埋下了內存泄漏的隱患 —— 因為團隊中的新手容易因這是個 Activity,而在日后誤將其依賴給組件中的其他成員。

現如今,我們可以直接在組件內部 點到為止 地監聽 LifecycleOwner 的狀態,從而規避這種不恰當的使用。

2.規避 為追溯事故來源 而 注入視圖控制器 的做法

當發生事故時,以往我們若想在組件中?追溯事故來源,同樣不得不從方法中直接注入 Activity 等,這同樣埋下了內存泄漏的隱患。現如今組件因實現了 DefaultLifecycleObserver,而得以通過生命周期回調方法中的 LifecycleOwner 參數,在方法作用域中 即可得知事故來源,無需更多帶有隱患的操作。

如果這么說還不理解的話,可具體參考 《為你還原一個真實的 Jetpack LifeCycler》 中提供的 GpsManager 案例,本文不再累述。

Jetpack LiveData

LiveData 的存在,主要是為了幫助?新手老手 都能不假思索地遵循 通過唯一可信源分發狀態 的標準化開發理念,從而使在快速開發過程中 難以追溯、難以排查、不可預期 的問題所發生的概率降低到最小。

LiveData 存在前的混沌世界

在 LiveData 面市前,我們分發狀態,多是通過 EventBus 或 Java Interface 來完成的。不管你是用于網絡請求回調的情況,還是跨頁面通信的情況。

那這造成了什么問題呢?首先,EventBus 只是純粹的 Bus,它?缺乏上述提到的 標準化開發理念 的約束,那么人們在使用這個框架時,容易因 去中心化 地濫用,而造成 諸如 毫無防備地收到 預期外的 不明來源的推送、拿到過時的數據 及 事件源追溯復雜度 為 n2 的局面。

并且,EventBus 本身缺乏 LifeCycler 的加持,存在生命周期管理的一致性問題。這是 EventBus 的硬傷,也是我拒絕使用 EventBus 的最主要因素。

對上述狀況不理解的,可具體參考《LiveData 鮮為人知的 身世背景 和 獨特使命》 中提供的 播放器狀態全局通知 的案例

LiveData 為什么能解決上述這些問題?

首先,LiveData 是在 Google 希望確立 標準化、規范化 的開發模式 —— 這樣一種背景下誕生的,因而為了達成這個艱巨的?使命,Google 十分克制地將其設計為,僅支持狀態的輸入和監聽,從而,它不得不?在單例的配合下,承上啟下地完成 狀態 從 唯一可信源 到 視圖控制器 的輸送。

(ViewModel 姑且也算是一種單例,一種工廠模式實現的偽單例。唯一可信源是指 生命周期獨立于 視圖控制器的 數據組件,通常是 單例 或共享 ViewModel)

這使得任何一次狀態推送,都可預期、都能方便地追溯來源,而不至于在 事件追溯復雜度為 n2 的迷宮中白費時間。(即,無論是從哪個視圖控制器發起的 對某個共享狀態改變的請求,狀態最終的改變 都由 作為唯一可信源的 單例或 SharedViewModel 來一對多地通知改變)

1dad1b73d4f0446fbedad462c1a5b031


并且,這種承上啟下的方式,使得單向依賴成為可能:單例無需通過 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學習PDF+架構視頻+面試文檔+源碼筆記

最后

感謝大家能耐著性子,看完我啰哩啰嗦的文章。

我愿與各位堅守在Android開發崗位的同胞們互相交流學習,共同進步!

在這里我也分享一份自己收錄整理的Android學習PDF+架構視頻+面試文檔+源碼筆記,還有高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料幫助大家學習提升進階,也節省大家在網上搜索資料的時間來學習,也可以分享給身邊好友一起學習

如果你有需要的話,可以點贊+收藏

歡迎關注微信公眾號【Android開發之家】更多精彩等你來

672c5c19111540b280d4726d8a298162


2ca5d656b0e946608db4de9d52e4d87a


推薦站點

  • 騰訊騰訊

    騰訊網(www.QQ.com)是中國瀏覽量最大的中文門戶網站,是騰訊公司推出的集新聞信息、互動社區、娛樂產品和基礎服務為一體的大型綜合門戶網站。騰訊網服務于全球華人用戶,致力成為最具傳播力和互動性,權威、主流、時尚的互聯網媒體平臺。通過強大的實時新聞和全面深入的信息資訊服務,為中國數以億計的互聯網用戶提供富有創意的網上新生活。

    www.qq.com
  • 搜狐搜狐

    搜狐網是全球最大的中文門戶網站,為用戶提供24小時不間斷的最新資訊,及搜索、郵件等網絡服務。內容包括全球熱點事件、突發新聞、時事評論、熱播影視劇、體育賽事、行業動態、生活服務信息,以及論壇、博客、微博、我的搜狐等互動空間。

    www.sohu.com
  • 網易網易

    網易是中國領先的互聯網技術公司,為用戶提供免費郵箱、游戲、搜索引擎服務,開設新聞、娛樂、體育等30多個內容頻道,及博客、視頻、論壇等互動交流,網聚人的力量。

    www.163.com
  • 新浪新浪

    新浪網為全球用戶24小時提供全面及時的中文資訊,內容覆蓋國內外突發新聞事件、體壇賽事、娛樂時尚、產業資訊、實用信息等,設有新聞、體育、娛樂、財經、科技、房產、汽車等30多個內容頻道,同時開設博客、視頻、論壇等自由互動交流空間。

    www.sina.com.cn
  • 百度一下百度一下

    百度一下,你就知道

    www.baidu.com
?
买北京单场