主頁 > 移動端開發 > 沉思篇-剖析Jetpack的ViewModel

沉思篇-剖析Jetpack的ViewModel

2023-06-14 10:17:31 移動端開發

ViewModel做為架構組件的三元老之一,是實作MVVM的有力武器,

ViewModel的設計目標

ViewModel的基本功能就是管理UI的資料,其實,從職責上來說,這又是對Activity和Fragment的一次功能拆分,以前存盤在它們內部的資料,需要它們自己處理創建,更新,存盤,恢復的所有程序,同時它們還要處理UI的資料系結,更新,影片等操作,職責的多元化就容易出現不好定位和除錯的問題,另外,Activity和Fragment作為UI的承載者,很多時候需要共享資料和復用功能,而UI的差異讓復用的粒度劃分很難把控,容易寫出擴展性差的代碼,基于這些痛點,ViewModel被設計出來了,
同時ViewModel還將保存資料的功能強化了——它將設備配置變更后資料保存和恢復自動化了,在UI生命周期內都能保證資料的有效性,這大大減少了樣板代碼的撰寫,提高了開發效率,

ViewModel的架構設計

ViewModel用了兩種粒度劃分來完成資料管理功能, 第一層是對ViewModel自身存盤資料的管理,目標就是完成ViewModel的創建,對應的抽象物體是ViewModelProvider.Factory,第二層則是對已存在的ViewModel組的管理,目標就是保證意外情況下ViewModel的有效性,對應的抽象物體是ViewModelStore,當然,這些都只是概念上的抽象,還需要一個粘合劑把它們的抽象層級體現出來,這就是ViewModelProvider,這三個主體類共同搭建了ViewModel的體系框架,剩下的類都是對這三個概念的補充和完善,接下來我將分別以這些抽象為主線,逐層分析它們的實作邏輯,

ViewModel的組管理

前面也提到過,ViewModelStore是完成組管理的,那么我們首先應該確定的是組的概念,也就是這些ViewModel都歸屬于誰的問題,這不難理解,要管理組,那就必須得找到組的主人啊,由此引申出了ViewModelStoreOwner,它代表著某個擁有組管理權限的物件,通過它提供的ViewModelStore物件就能對里面的ViewModel進行管理了,同時這些ViewModel也就共同形成了組,所以ViewModelStoreOwner其實就是組的抽象物體,它代表著某個組,也是管理分組的單位,
ViewModel有兩個默認實作的組——ComponentActivity和Fragment,也就是說ComponentActivity和Fragment都實作了ViewModelStoreOwner這個介面,
先來看ComponentActivity的實作,根據介面,首先查看介面方法getViewModelStore的實作,里面主要涉及到兩個物件,一個就是ViewModelStore的參考mViewModelStore,另一個就很有意思了,它是一個NonConfigurationInstances物件,這是一個簡單類,就是保存ViewModelStore物件的,那么它特殊在什么地方呢?它是onRetainNonConfigurationInstance方法的回傳物件,

插一個課外知識科普,onRetainNonConfigurationInstance是Activity的一個方法,這個方法是設備配置發生變化(如橫豎屏切換的時候)時被系統自動呼叫的,用于用戶保存資料,只要這個方法回傳的物件,在設備配置放生改變時都不會被銷毀,稍后在重建完成后,可以通過getLastNonConfigurationInstance方法獲取到,

接著回到getViewModelStore的實作,剛才說到NonConfigurationInstances物件,它是通過呼叫getLastNonConfigurationInstance方法獲得的,如果方法回傳了有效的物件,說明Activity被重建了,就直接獲取保存在NonConfigurationInstances物件中的值,然后更新mViewModelStore,否則就說明還沒有有效的ViewModelStore物件,則直接創建,從這個邏輯不難看出,我們的ViewModel不會隨著設備變化而重建,這正好滿足了我們的設計目標,那么對于Fragment,它的實作又是怎樣的呢,
Fragment的實作比較曲折,它直接委托給了FragmentManager,又委托給了FragmentManagerViewModel的getViewModelStore方法,方法實作也很簡單,就是對HashMap查找,沒有就創建新的,這顯然不是我們想看到的,因為這里并沒有和Activity類似的處理狀態變更的邏輯,那么唯一的突破點就是那個HashMap物件了,搜索一圈發現,它會作為getSnapshot方法的回傳值回傳,有點Activity那味了,往上回溯,會發現它最終就是作為不銷毀的物件,在Fragment銷毀前保存下來了,
以上就是兩種應用場景下ViewModelStore的創建邏輯,另外,還有清除邏輯沒有講到,這個邏輯本質上就是呼叫ViewModelStore的clear方法,唯一的問題就是確定呼叫時機,具體來說就是,Activity通過注冊Lifecycle的狀態監聽,在Lifecycle.Event.ON_DESTROY的時候,呼叫了clear方法,而Fragment則是繼續通過FragmentManager的desctory方法作為呼叫的入口點,在FragmentManagerViewModel里完成了方法呼叫,
總結一下,ViewModelStoreOwner是對ViewModel組的一種抽象,雖然對應著兩個不同的實作,但是殊途同歸,最終的目的就是保證在設備配置發生變化的時候對應ViewModelStore物件的有效性, 從而保證ViewModel物件的有效性,同時在真正需要銷毀的時候做好清理作業,這就是這ViewModel的組管理功能,

ViewModel的創建管理

ViewModel用ViewModelProvider.Factory來管理創建程序,具體來說就是怎樣根據一個ViewModel子類的類資訊創建對應的物件,這有兩個難點——必要的依賴注入、資料的恢復,對于依賴注入,ViewModel還是耍了老把戲,和創建ViewModelStore類似,提供了HasDefaultViewModelProviderFactory的一個抽象,把依賴注入轉移到了ComponentActivity和Fragment中,之所以這么做,是因為在創建ViewModel的程序中,可能需要使用到Application和Bundle等資訊,而這些資訊是只能在在Activity和Fragment中才能獲取到的,資料恢復則是關注怎樣利用現有的資料將物件恢復到原來的狀態,當然這些程序其實都可以沒有,不需要傳遞Application或者Bundle物件,不需要恢復ViewModel狀態,則庫提供了默認的實作,就是簡單的呼叫反射創建物件而已,
針對剛才說的各種情況,ViewModelProvider.Factory有多個實作,那么實際上它到底是使用哪個實作呢,我們得從ViewModelProvider中尋找答案,在它的構造方法里,會對ViewModelStoreOwner做型別判斷,假如它是HasDefaultViewModelProviderFactory的實體,則使用實體回傳的物件,否則默認的實作,結合上面的分析,讓我們繼續到ComponentActivity和Fragment中尋找答案,不看不知道,一看嚇一跳,它們竟然都是使用了SavedStateViewModelFactory類,那么我們一起來看看它是怎么實作的吧,
在構建SavedStateViewModelFactory物件的時候,會傳入三個物件——Application,SavedStateRegistryOwner,Bundle,這三個物件中最重要的就是第二個,它的主要功能就是提供在SavedStateRegistry物件,這個物件會在合適的時候保存資料,然后在合適的時候再恢復過來,它也是生命周期感知的組件,在它的create方法里,也是通過反射構建ViewModel物件的,唯一的不同就是反射多了個引數,接著往下看,最侄訓利用這些資訊構造出SavedStateHandle物件,這個物件就是真正對我們當前創建的ViewModel物件有用的資訊,SavedStateHandle提供了根據鍵值對保存資料的方法,也提供了查詢方法,所以ViewModel可以根據這個物件,恢復自己的LiveData資料,最重要的,這個類還提供了LiveData的另一個子類SavingStateLiveData,能自動處理資料保存的問題,
一句話總結,ComponentActivity和Fragment會使用SavedStateViewModelFactory物件作為ViewModelProvider中的Factory來創建ViewModel,只要ViewModel提供了帶有Application或者SavedStateHandle的構造方法,就能享受從Bundle中恢復資料的便利,

ViewModel的粘合劑ViewModelProvider

為什么說ViewModelProvider是粘合劑呢?因為這個類就做了一件事,把ViewModelStore和ViewModelProvider.Factory組合起來,實作了一個叫get的方法,這個方法的內部實作就是有兩個步驟,

  1. 呼叫ViewModelStore的get方法查詢是否有創建好的物件,如果有就回傳,方法結束,否則進入步驟2,

  2. 呼叫ViewModelProvider.Factorycreate方法創建物件,并將之保存到ViewModelStore中,

所以當我們要使用ViewModel的時候,通常是創建ViewModelProvider物件,然后呼叫get方法獲取真正的ViewModel物件,這樣,我們的物件就具備了正確處理設備配置變更的能力,

ViewModel的Fragment間通信功能

根據前面的梳理,我們知道,ViewModelStore是管理某個ViewModel組的,只要我們保證ViewModelStore存在,我們就可以保證ViewModel存活,再反推一步,要保證ViewModelStore存活,我們就要保證ViewModelStoreOwner在不同的地方都能回傳同一個ViewModelStore物件,而ComponentActivity和Fragment是都實作了這個介面的,結合Activity的生命周期通常是大于Fragment這一事實,不難得出結論——在某個Fragment里面,用Activity物件創建ViewModelProvider物件,就能保證獲取到和Activity一樣的ViewModelStore物件,也就能保證獲取到相同的ViewModel物件,只要Activity沒有銷毀,該Activity下的所有Fragment都能獲取到相同的ViewModel物件,然后通過更改狀態能方式完成通信,

到此,對ViewModel的分析告一段落了,對創建程序的兩次抽象是我覺得最精彩的環節,另外對現有條件(Activity和Fragment的生命周期)的利用也是它獨到之處,真的是受益匪淺,青山不改,綠水長流,咱們下期見!
viewmodel

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/555150.html

標籤:其他

上一篇:插件化工程R檔案瘦身技術方案 | 京東云技術團隊

下一篇:返回列表

標籤雲
其他(160965) Python(38226) JavaScript(25495) Java(18235) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7251) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4593) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2435) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1966) Web開發(1951) C++(1939) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 沉思篇-剖析Jetpack的ViewModel

    > ViewModel做為架構組件的三元老之一,是實作MVVM的有力武器。 ### ViewModel的設計目標 ViewModel的基本功能就是管理UI的資料。其實,從職責上來說,這又是對Activity和Fragment的一次功能拆分。以前存盤在它們內部的資料,需要它們自己處理創建,更新,存盤, ......

    uj5u.com 2023-06-14 10:17:31 more
  • 插件化工程R檔案瘦身技術方案 | 京東云技術團隊

    隨著業務的發展及版本迭代,客戶端工程中不斷增加新的業務邏輯、引入新的資源,隨之而來的問題就是安裝包體積變大,前期各個業務模塊通過無用資源刪減、大圖壓碩訓轉上云、AB實驗業務邏輯下線或其他手段在降低包體積上取得了一定的成果。 ......

    uj5u.com 2023-06-14 10:17:13 more
  • 沉思篇-剖析Jetpack的ViewModel

    > ViewModel做為架構組件的三元老之一,是實作MVVM的有力武器。 ### ViewModel的設計目標 ViewModel的基本功能就是管理UI的資料。其實,從職責上來說,這又是對Activity和Fragment的一次功能拆分。以前存盤在它們內部的資料,需要它們自己處理創建,更新,存盤, ......

    uj5u.com 2023-06-14 10:16:47 more
  • 插件化工程R檔案瘦身技術方案 | 京東云技術團隊

    隨著業務的發展及版本迭代,客戶端工程中不斷增加新的業務邏輯、引入新的資源,隨之而來的問題就是安裝包體積變大,前期各個業務模塊通過無用資源刪減、大圖壓碩訓轉上云、AB實驗業務邏輯下線或其他手段在降低包體積上取得了一定的成果。 ......

    uj5u.com 2023-06-14 10:16:29 more
  • 直播回顧|走進元服務,攜手小強停車探索鴻蒙新流量陣地

    本期直播《“元”來如此,“服務”直達——揭秘鴻蒙新流量陣地》聚焦**元服務**的**商業流量價值**,介紹元服務提供的服務直達和卡片動態變化等**輕量化服務**。網約停車旗艦平臺小強停車做客直播間,分享小強停車在HarmonyOS生態中,如何通過元服務為廣大用戶帶來更加便捷易用的線上預約停車體驗。快 ......

    uj5u.com 2023-06-13 09:08:45 more
  • 直播回顧|走進元服務,攜手小強停車探索鴻蒙新流量陣地

    本期直播《“元”來如此,“服務”直達——揭秘鴻蒙新流量陣地》聚焦**元服務**的**商業流量價值**,介紹元服務提供的服務直達和卡片動態變化等**輕量化服務**。網約停車旗艦平臺小強停車做客直播間,分享小強停車在HarmonyOS生態中,如何通過元服務為廣大用戶帶來更加便捷易用的線上預約停車體驗。快 ......

    uj5u.com 2023-06-13 08:58:07 more
  • 【有獎調研】互聯網新型社交,華為在找“元服務搭子”,快來集合!

    “聊技術無話不談,一起來吹吹元服務!暢聊你對元服務的想法,說不定,你就能撬動元服務的爆發增長!” 元服務(即原子化服務)是華為“輕量化”服務的新物種,可提供全新的服務和互動方式,讓應用化繁為簡,讓服務觸手可及!基于鴻蒙萬能卡片,元服務可實作應用功能在桌面“永遠打開”,實作智能推薦、服務直達! 而在元 ......

    uj5u.com 2023-06-13 08:36:02 more
  • 【有獎調研】互聯網新型社交,華為在找“元服務搭子”,快來集合!

    “聊技術無話不談,一起來吹吹元服務!暢聊你對元服務的想法,說不定,你就能撬動元服務的爆發增長!” 元服務(即原子化服務)是華為“輕量化”服務的新物種,可提供全新的服務和互動方式,讓應用化繁為簡,讓服務觸手可及!基于鴻蒙萬能卡片,元服務可實作應用功能在桌面“永遠打開”,實作智能推薦、服務直達! 而在元 ......

    uj5u.com 2023-06-13 08:35:37 more
  • 構建docker編譯AOSP

    ## 配置AOSP docker編譯環境 ### 1.安裝docker ``` curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh ``` 參考:[github](https://github.com/docker/ ......

    uj5u.com 2023-06-09 08:24:52 more
  • 構建docker編譯AOSP

    ## 配置AOSP docker編譯環境 ### 1.安裝docker ``` curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh ``` 參考:[github](https://github.com/docker/ ......

    uj5u.com 2023-06-09 08:24:32 more