改造 Android 官方架構元件 ViewModel

JessYan發表於2017-12-14

原文地址: https://juejin.im/post/5a31f6b951882503eb4b4b21

前言

Android 官方架構元件在今年 5 月份 Google I/O 大會上被公佈, 直到 11 月份一直都是測試版, 由於工作比較繁忙, 期間我只是看過類似的文章, 但沒有在實際專案中使用過, 更沒有看過原始碼, 所以對這幾個元件的使用很是生疏, 同時也覺得這幾個元件非常高大上, 非常神祕!

直到 11 月份 Android 官方架構元件正式版釋出, 並且 Google 也在 Support Library v26.1.0 以後的版本中內嵌了 Android 官方架構元件中的生命週期元件, 我想, 這是趨勢, 既然 Google 這麼推崇, 那我也是時候學習一波並將它們引入 MVPArms 框架了

Github : 你的 Star 是我堅持的動力 ✊

簡單介紹

因為想將 Android 官方架構元件引入 MVPArms 框架之中, 所以我認真學習了 Android 官方架構元件中除了 Room 之外的所有原始碼, 以考察是否整個元件都適合引入 MVPArms 框架

在學習完原始碼過後, 發現 Android 官方架構元件其實並沒有想象的那麼高深, 原理反而是我們在日常開發中都會用到的知識點, 那我就在文章的開頭先簡單的介紹下 Android 官方架構元件中的這幾個元件

Lifecycles

生命週期元件是 Android 官方架構元件中的核心元件, 它可以使各種例項作為觀察者與 ActivityFragment 等具有生命週期特性的元件繫結在一起, LiveDataViewModel 都是基於此元件, 簡而言之就是, 你將需要繫結生命週期的例項註冊給該元件, 該元件就會在你指定的某個生命週期方法執行時通知這個例項

應用場景很多, 比如之前在 MVP 架構中, 你需要在 Activity 執行 onCreate 時, 讓 Presenter 初始化一些操作, 這時就不用在 ActivityonCreate 中再呼叫 Presenter 的某個初始化方法了, 直接使用官方的生命週期元件即可完成, 在 Activity 執行 onDestroy 時需要釋放一些物件的資源, 也可以使用到生命週期元件

LiveData

LiveData 具有兩個功能, 第一個功能是觀察者模式, 在 Value 發生變化時通知之前註冊的所有觀察者, 第二功能是基於生命週期元件與 ActivityFragment 等具有生命週期特性的元件繫結在一起, 在生命週期發生改變時停止或恢復之前的事件

簡而言之就是, 當某個頁面請求網路資料成功後需要同步 UI, 但這個頁面已經不可見, 這時就會停止同步 UI 的操作

ViewModel

ViewModel 有兩個功能, 第一個功能可以使 ViewModel 以及 ViewModel 中的資料在螢幕旋轉或配置更改引起的 Activity 重建時存活下來, 重建後資料可繼續使用, 第二個功能可以幫助開發者輕易實現 FragmentFragment 之間, ActivityFragment 之間的通訊以及共享資料

淺析官方架構元件

用法就不多說了, 此類文章和 Demo 太多了, 明白了它們的功能和應用場景後, 我們才知道它們是否真的適合自己的需求, 而不是盲目跟風, 下面我就來分析下我是如何考察新技術, 以及如何判斷這些新技術是否有必要應用到自己的專案中

Lifecycles

上面介紹了生命週期元件的功能, 這裡就來分析一下生命週期元件是否有必要引入我的框架 MVPArms

說到生命週期我就想到了我之前在 傳統MVP用在專案中是真的方便還是累贅? 中討論的一個內容

現在市面上流行的 MVP 架構有兩種, 第一種是將 Activity 或 Fragment 作為 View, 抽象一個 Presenter 層出來, 第二種是將 Activity 或 Fragment 作為 Presenter, 抽象一個 View 層出來

第一種型別代表的框架有 MVPArms, 第二種型別代表的框架有 TheMVP, 當然第一種型別的 MVP 架構在市面上用的是最多的, 那麼第二種型別的優點是什麼呢?

我在上面這篇文章也說過, 主要優勢有兩個, 方便重用View, 以及 可直接與 Activity 或 Fragment 的生命週期做繫結, 這樣就可以直接使用 Activity 或 Fragment 的生命週期, 不用再去做多餘的回撥, 當然也有缺點, 我在文章中也有介紹, 有興趣的可以去看看

第一種型別的 MVP 架構是不具有可以和 ActivityFragment 的生命週期直接做繫結的優勢的, 所以很是嫉妒第二種型別的 MVP 架構, 這也是兩種型別的 MVP 架構最大的區別, 但你想的沒錯, 現在使用生命週期元件就可以使第一種型別的 MVP 架構很輕易的具有繫結生命週期的優勢, 現在第一種型別的 MVP 架構將如虎添翼

經過以上的分析, 我認為生命週期元件對於我的框架來說是很有必要的, 這將使日常開發更加便捷

LiveData

LiveDataRxJava 都是基於觀察者模式, 功能上也有重合, Google 在官方文件上也明確表示, 如果你正在使用 RxJava, Agera 等類似功能的庫, 只要你能正確的處理資料流的生命週期, 就完全可以繼續使用它們來替代 LiveData

Note: If you are already using a library like RxJava or Agera, you can continue using them instead of LiveData. But when you use them or other approaches, make sure you are handling the lifecycle properly such that your data streams pause when the related LifecycleOwner is stopped and the streams are destroyed when the LifecycleOwner is destroyed. You can also add the android.arch.lifecycle:reactivestreams artifact to use LiveData with another reactive streams library (for example, RxJava2).

從官方文件可以看出 Google 對此的建議就是 RxJava, Agera, LiveData 等類似功能的庫, 你只使用一個即可

選擇 RxJava 還是 LiveData ?

LiveDataRxJava 的功能的確過於重合, 我也十分贊同 Google 官方的建議, 兩者之中選擇其一就可以了, 沒必要兩者都引入專案, 而 MVPArms 框架, 也正好引入了 RxJava, 所以我也來分析分析在 MVPArms 框架中該選擇 LiveData 還是 RxJava?

於是我認真的研究了其原始碼, LiveData 具有兩個功能, 通知觀察者更新資料和根據生命週期停止和恢復之前的事件, 而 Rxjava 加上 RxLifecycle, RxJava 加上 AutoDispose, 或 Rxjava 加上生命週期元件, 也可以輕易做到根據生命週期停止和恢復之前的事件, 在配上 Rxjava 強大的操作符, LiveData 能做的事 RxJava 都能做, LiveData 不能做的事 RxJava 也能做

並且 RxJava 不僅僅只是 RxJava, 他還是一個龐大的生態鏈, 他還有 RxCache, RxLifecycle, RxAndroid, RxPermission, Retrofit-Adapter 等大量並且強大的衍生庫, 我們離開它做很多事都非常不便, 剛剛出生, 羽翼未豐的 LiveData 相比於 RxJava 將沒有任何優勢, 甚至顯得非常簡陋

因此 LiveDataRxJava 之間如果只能選擇一個的話, 我沒有任何理由選擇 LiveData

ViewModel

ViewModel 中有一個功能讓我十分驚豔, 也十分好奇, 它可以使 ViewModel 以及 ViewModel 中的資料在螢幕旋轉或配置更改引起的 Activity 重建時存活下來, 重建後資料可繼續使用, 這個功能十分實用且十分重要, 因為之前也沒有一個官方解決方案, 所以我覺得很有必要將這個功能引入 MVPArms 框架

同樣另外一個功能, 它還可以幫助開發者輕易實現 FragmentFragment 之間, ActivityFragment 之間的通訊以及共享資料, 同樣也正是我所需要的官方解決方案

但在我繼續深入研究, 準備將它引入到專案中時, 卻發現 Google 將這個功能做了高度封裝並限制了它的使用範圍, 只能用於 ViewModel

但我想 Google 既然能讓 MVVM 框架中的 ViewModel 具有這些功能, 那我為什麼不能將這個功能擴充套件出來提供給 MVP 框架中的 Presenter, 乃至其他更多的模組?

於是我認真的研究了其原始碼, 準備通過修改原始碼並封裝成庫的方式, 讓更多的開發者在更多的場景下能夠使用到這些功能

改造 ViewModel 元件

要想改造 ViewModel 元件 自然要對它的整個原始碼分析一遍, 知道其原理, 才知道如何下手

分析原始碼

篇幅有限, 就來簡單的分析下原始碼把, 原始碼其實也就幾個類, 經過了層層封裝, 核心程式碼就在一個叫做 HolderFragmentFragment 中,

在我看來 ViewModel 元件 的核心原理也就是 HolderFragment 中的一行程式碼實現的:

setRetainInstance(true);
複製程式碼

setRetainInstance(boolean)Fragment 中的一個方法, 我想很多人應該都知道這個方法的意義

簡單來說將這個方法設定為 true 就可以使當前 FragmentActivity 重建時存活下來, 如果不設定或者設定為 false, 當前 Fragment 會在 Activity 重建時同樣發生重建, 以至於被新建的物件所替代

意思是隻要將這個方法設定為 true, Fragment 以及 Fragment 之中的所有資料都會在 Activity 重建時存活下來

這時我們在 setRetainInstance(boolean) 為 true 的 Fragment 中放一個專門用於儲存 ViewModelMap, 自然 Map 中所有的 ViewModel 都會倖免於 Activity 重建

於是我們讓 Activity, Fragment 都繫結一個這樣的 Fragment, 將 ViewModel 存放到這個 FragmentMap 中, ViewModel 元件 就這樣實現了

如何改造

想要知道如何改造, 那我們就要明確這次改造的最終目的是什麼, 我們的目的就是要讓 ViewModel 元件 能用於 Presenter, 乃至其他更多的模組, 不止是用於 ViewModel

那為什麼 Google 官方的 ViewModel 元件 不能用於其他模組呢, 通過閱讀原始碼可以知道, 是因為 Google 把上文提到的 Map, 封裝了起來, 並沒有提供出去, 並且限制了 ViewModel 的構建方式

ViewModel 元件 讓一個新的 ViewModel 必須繼承於它的基類, 並且讓開發者必須提供一個 Factory 指明當前 ViewModel 的構建方式, ViewModel 元件 會在合適的時機, 主動去根據 Factory 構建 ViewModel 例項, 並放入 Map

這時整個構建過程都被 ViewModel 元件 掌控並被限制於 ViewModel, 所以我需要做的就是將 MapViewModel 的構建方式擴充套件出來, 將更多的控制權交給外部的開發者

實踐

經過上面的分析, 思路和方案都有了, 接下來就剩下如何把思路和方案實現了

於是我結合上文分析的思路和方案對官方原始碼進行了改造並做了適當的優化, LifecycleModel 就這樣誕生了

這篇文章主要還是講在完成一個目標前, 在從 0 到 1 期間進行的思路和分析的過程, 至於細節你如果感興趣的話還是去看我的原始碼把, 哈哈, 註釋很詳細哦!

Github : 你的 Star 是我堅持的動力 ✊

總結

一個新技術是否真的適合自己還是需要自己去考察, 不應該盲目跟風, 如果你只知道這個技術很火然後去用它, 不知道為什麼用它, 用它的好處, 那你就會一直陷入被動學習的窘境, 一直在學習, 但是總覺得自己跟不上時代的進步, 擔驚受怕, 這是現代技術人大部分都存在的處境

至於最近鬧的沸沸揚揚的簡書 飽醉豚 事件, 自從簡書 CEO 站臺後, 已經不再是當事人一個人的事, 而是關乎到簡書整個平臺, 既然這個 CEO 這麼傲氣, 這個平臺都不在乎我們這個群體, 我們也不再去關注這個平臺就是了, 流量是跟著原創作者走還是跟著平臺, 自己心裡沒點逼數嗎?

簡書以及簡書 CEO 最好做出深刻的道歉, 否則我也會離開簡書 (好像我更文頻率也不是很高把? 咳咳... 我主打的是質量! 質量! 不是數量, 逃~)

踩坑

在實際專案中使用 ViewModel 元件 時我也遇到了一些問題, 浪費了我很多時間, 所以有必要分享出來讓大家少走彎路

通過 Activity 獲取 ViewModel 時遇到的坑:

  • 在 Application.ActivityLifecycleCallbacks 中的 onActivityCreated 方法中獲取 ViewModel 時, Activity 每重建一次, 獲取的 ViewModel 都是重新構建後的新例項, 並不能讓 ViewModel 以及 ViewModel 中的資料倖免於 Activity 重建, 所以不要此方法中獲取 ViewModel

  • 在 Activity 的 onDestroy 方法中不能獲取 ViewModel, 會報錯

通過 Fragment 獲取 ViewModel 時遇到的坑:

  • 在 FragmentManager.FragmentLifecycleCallbacks 中的 onFragmentAttached 方法中獲取 ViewModel 時也會出現和 Activity 一樣的情況, 獲取的 ViewModel 是重新構建後的新例項, ViewModel 以及 ViewModel 中的資料不能倖免於 Activity 重建, 所以也不要此方法中獲取 ViewModel

  • 在 FragmentManager.FragmentLifecycleCallbacks 中的 onFragmentDestroyed 方法中也不能獲取 ViewModel, 會報錯

  • 在 Fragment 的 onDestroy 方法中不能獲取 ViewModel, 會報錯

公眾號

掃碼關注我的公眾號 JessYan,一起學習進步,如果框架有更新,我也會在公眾號上第一時間通知大家

改造 Android 官方架構元件 ViewModel


Hello 我叫 JessYan,如果您喜歡我的文章,可以在以下平臺關注我

-- The end

相關文章