引言
關(guān)于 ViewModel 灯节,Android 開發(fā)的小伙伴應(yīng)該都非常熟悉兔甘,無論是新項目還是老項目,基本都會使用到启泣。而且 ViewModel 作為 JetPack
核心組件决摧,其本身也更是承擔(dān)著不可或缺的作用亿蒸。因此凑兰,了解 ViewModel 的設(shè)計思想是每個應(yīng)用層開發(fā)者必不可缺的基本功。
隨著這兩年 ViewModel
的逐步迭代边锁,比如 SaveStateHandle 的加入等姑食,ViewModel 也已經(jīng)不是最初版本的樣子。要完全理解其設(shè)計體系茅坛,往往也要伴隨這其他組件的基礎(chǔ)音半,所以并不是特別容易能被開發(fā)者吃透。
故本篇將以最新視角開始贡蓖,與你一起曹鸠,用力一瞥 ViewModel 的設(shè)計原理。
本文對應(yīng)的組件版本:
- Activity-ktx-1.5.1
- ViewModel-ktx-2.5.1
本篇定位中等斥铺,將從背景與使用方式開始彻桃,再到源碼解讀。由淺入深晾蜘,解析
ViewModel
的方方面面邻眷。
導(dǎo)航
學(xué)完本篇,你將了解或明白以下內(nèi)容:
-
ViewModel
的使用方式剔交; -
SavedStateHandle
的使用方式肆饶; -
ViewModel
創(chuàng)建與銷毀流程; -
SavedStateHandle
創(chuàng)建流程岖常;
好了驯镊,讓我們開始吧! ??
基礎(chǔ)概念
在開始本篇前,我們先解釋一些基礎(chǔ)概念竭鞍,以便更加清晰的了解后續(xù)的狀態(tài)保存相關(guān)板惑。
何謂配置變更?
配置變更指的是,應(yīng)用在運行時笼蛛,內(nèi)置的配置參數(shù)變更從而觸發(fā)的Activity重新創(chuàng)建洒放。
常見的場景有:旋轉(zhuǎn)屏幕、深色模式切換滨砍、屏幕大小變化往湿、更改了默認(rèn)語言或者時區(qū)、更改字體大小或主題顏色等惋戏。
何謂異常重建领追?
異常重建指的是非配置變更情況下導(dǎo)致的 Activity
重新創(chuàng)建。
常見場景大多是因為 內(nèi)存不足响逢,從而導(dǎo)致后臺應(yīng)用被系統(tǒng)回收 绒窑,當(dāng)我們切換到前臺時,從而觸發(fā)的重建舔亭,這個機制在Android中為 Low Memory Killer
機制些膨,簡稱 LMK
蟀俊。
可以在開發(fā)者模式,限制后臺任務(wù)數(shù)為1订雾,從而測試該效果肢预。
ViewModel存在之前的世界
在 ViewModel
出現(xiàn)之前,對于 View
邏輯與數(shù)據(jù),我們往往都是直接存在 Activity
或者 Fragment
中洼哎,優(yōu)雅一點烫映,會細分到具體的單獨類中去承載。當(dāng)配置變更時噩峦,無可避免锭沟,會觸發(fā)界面重繪。相應(yīng)的识补,我們的數(shù)據(jù)在沒有額外處理的情況下族淮,往往也會被初始化,然后在界面重啟時重新加載李请。
但如果當(dāng)前頁面需要維護某些狀態(tài)不被丟失呢瞧筛,比如 選擇、上傳狀態(tài) 等等? 此時問題就變得棘手起來导盅。
稍有經(jīng)驗同學(xué)會告訴你,在 onSaveInstanceState 中重寫揍瑟,使用bundle去存儲相應(yīng)的狀態(tài)鞍追???
但狀態(tài)如果少點還可以绢片,多一點就非常頭痛滤馍,更別提包含繼承關(guān)系的狀態(tài)保存。??
所以底循,不出意外的話巢株,我們 App 的 Activity-manifest 中通常默認(rèn)都是下列寫法:
android:configChanges="keyboard|orientation|uiMode|..."
這也是為啥Android程序普遍不支持屏幕旋轉(zhuǎn)的一部分原因,從源頭扼殺因部分配置變更導(dǎo)致的狀態(tài)丟失問題熙涤。??保命
VideModel存在之后的世界
隨著 ViewModel
組件推出之后阁苞,上述因配置變更而導(dǎo)致的狀態(tài)丟失問題就迎刃而解。
ViewModel
可以做到在配置變更后依然持有狀態(tài)祠挫。所以那槽,在現(xiàn)在的開發(fā)中,我們開始將 View數(shù)據(jù) 與 邏輯 藏于 ViewModel
中等舔,然后對外部暴漏觀察者骚灸,比如我們常常會搭配 LiveData
一起使用,以此更容易的保持狀態(tài)同步慌植。
關(guān)于 ViewModel
的生命周期甚牲,具體如下圖所示:
雖然 ViewModel
非常好用义郑,但 ViewModel
也不是萬能,其只能避免配置變更時避免狀態(tài)丟失丈钙。比如如果我們的App是因為 內(nèi)存不足 而被系統(tǒng)kill 掉魔慷,此時 ViewModel
也會被清除??。
不過對于這種情況著恩,仍然有以下三個方法可以依然保存我們的狀態(tài):
- 重寫
onSaveInstanceState()
與onRestoreInstanceState()
; - 使用
SavedState
,本質(zhì)上其實還是onSaveInstanceState()
院尔; - 使用
SavedStateHandle
,本質(zhì)上是依托于SaveState
的實現(xiàn);
上述的后兩種都是隨著 JetPack 逐步被推出喉誊,可以理解為是對原有的onSavexx的封裝簡化邀摆,從而使其變得更易用。
關(guān)于這三種方法伍茄,我們會在 SavedStateHandle
流程解析中再進行具體敘述栋盹,這里先提出來,留個伏筆敷矫。
ViewModel使用方式
作為文章的開始例获,我們還是要先聊一聊 ViewModel
的使用方式,如下例所示:
當(dāng)然曹仗,你也可以選擇引入 activity-ktx ,從而以更簡便的寫法去寫:
implementation 'androidx.activity:activity-ktx:1.5.1' private val mainModel by viewModels<MainViewModel>()
示例比較簡單榨汤,我們創(chuàng)建了一個 ViewModel
,如上所示怎茫,并在 MainActivity
的 onCreate() 中進行了初始化收壕。
這也是我們?nèi)粘5氖褂梅绞剑唧w我們這里就不再做闡述轨蛤。
SavedStateHandle使用方式
我們知道蜜宪,ViewModel
可以處理因為配置更改而導(dǎo)致的的狀態(tài)丟失,但并不保證異常終止的情況祥山,而官方的 SavedStateHandle
正是用于這種情況的解決方式圃验。
SavedStateHandle
,如名所示,用于保存狀態(tài)的手柄缝呕。再細化點就是澳窑,用于保存狀態(tài)的工具,從而配合 ViewModel
而使用岳颇,其內(nèi)部使用一個 map 保存我們要存儲的狀態(tài)照捡,并且其本身使用 operator
重載了 set() 與 get() 方法,所以對于我們來說话侧,可以直接使用 **鍵值對 ** 的形式去操作我們要保存的狀態(tài)栗精,這也是官方為什么稱 SavedStateHandle
是一個 具有鍵值映射Map 特性的原因。
在 Fragment1.2 及 Activity1.1.0 之后,
SavedStateHandle
可以作為 ViewModel 的構(gòu)造函數(shù),從而反射創(chuàng)建帶有SavedStateHandle
的 ViewModel 悲立。
具體使用方式如下:
我們在 MainViewModel
構(gòu)造函數(shù)中新增了一個參數(shù) state:SavedStateHandle ,這個參數(shù)在 ViewModel
初始化時鹿寨,會幫我們自動進行注入。從而我們可以利用 SavedStateHandle
以key-value的形式去保存一些 自定義狀態(tài) ,從而在進程異常終止薪夕,Act重建后脚草,也能獲取到之前保存的狀態(tài)。
至于為什么能實現(xiàn)保存狀態(tài)呢原献?
主要是因為 SavedStateHandle
內(nèi)部默認(rèn)有一個 SavedStateRegistry.SavedStateProvider 狀態(tài)保存提供者對象馏慨,該對象會在我們創(chuàng)建ViewModel
時綁定到 SavedStateRegistry 中,從而在我們 Activity
異常重建時做到狀態(tài)的 恢復(fù) 與 **綁定 ** (通過重寫 onSavexx()
與 onCreate()
方法監(jiān)聽)姑隅。
關(guān)于這部分內(nèi)容写隶,我們下面的源碼解析部分也會再聊到,這里我們只需要知道是這么回事即可讲仰。
ViewModel源碼解析
本章節(jié)慕趴,我們將從 ViewModelProvider() 開始,理清 ViewModel
的 創(chuàng)建 與 銷毀 流程鄙陡,從而理解其背后的 [魔法]冕房。
不過 ViewModel 的源碼其實并不是很復(fù)雜,所以別擔(dān)心??趁矾。
仔細想想耙册,要解析ViewModel的源碼,應(yīng)該從哪里入手呢愈魏?
ViewModelProvider(this).get(MainViewModel::class.java)
最簡單的方式還是初始化這里觅玻,所以我們直接從 ViewModelProvider() 初始化開始->
ViewModelProvider(this)
public constructor(owner: ViewModelStoreOwner)
: this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
相應(yīng)的,這里開始培漏,我們就涉及到了三個方面,即 viewModelStore 胡本、 Factory牌柄、 Exras 。所以接下來我們就順藤摸瓜侧甫,分別看看這三處的實現(xiàn)細節(jié)珊佣。
owner.viewModelStore
ViewModelStoreOwner 顧名思義,用于保存 ViewModelStore
對象披粟。
而 ViewModelStore
是負(fù)責(zé)維護我們 ViewModel
實例的具體類咒锻,內(nèi)部有一個 map 的合集,用于保存我們創(chuàng)建的所有 ViewModel
守屉,并對外提供了 clear()
方法惑艇,以 便于非配置變更時清除緩存 。
defaultFactory(owner)
該方法用于初始化 ViewModel
默認(rèn)的創(chuàng)造工廠?? 。默認(rèn)有兩個實現(xiàn)滨巴,前者是 HasDefaultViewModelProviderFactory 思灌,也是我們 Fragment
或者 ComponentActivity
都默認(rèn)實現(xiàn)的接口,而后者是是指全局 NewInstanceFactory 恭取。
兩者的不同點在于泰偿,后者只能創(chuàng)建 空構(gòu)造函數(shù) 的 ViewModel
,而前者沒有這個限制蜈垮。
示例源碼:
HasDefaultViewModelProviderFactory 在 ComponentActivity 中的實現(xiàn)如下:
defaultCreationExtras(owner)
用于輔助 ViewModel
初始化時需要傳入的參數(shù)耗跛,具體源碼如下:
如上所示,默認(rèn)有兩個實現(xiàn)攒发,前者是 HasDefaultViewModelProviderFactory ,也就是我們 ComponentActivity
實現(xiàn)的接口,具體的實現(xiàn)如下:
默認(rèn)會幫我們注入 application
以及 intent
等调塌,注意這里還默認(rèn)使用了 getIntent().getExtras() 作為 ViewModel
的 默認(rèn)狀態(tài) ,如果我們 ViewModel
構(gòu)造函數(shù)中有 SavedStateHandle
的話晨继。
更多關(guān)于 CreationExtras 可以了解這篇 創(chuàng)建 ViewModel 的新方式烟阐,CreationExtras 了解一下?
get(ViewModel::xx)
從緩存中獲取現(xiàn)有的 ViewModel
或者 反射創(chuàng)建 新的 ViewModel
紊扬。
示例源碼如下:
當(dāng)我們使用 get() 方法獲取具體的 ViewModel
對象時蜒茄,內(nèi)部會先利用 當(dāng)前包名+ViewModel類名 作為 key
,然后從 viewModelStore
中取餐屎。如果當(dāng)前已創(chuàng)建檀葛,則直接使用;反之則調(diào)用我們的 ViewModel工廠 create() 方法創(chuàng)建新的 ViewModel
腹缩。 創(chuàng)建完成后屿聋,并將其保存到 ViewModelStore
中。
create(modelClass,extras)
具體的創(chuàng)造邏輯里藏鹊,這里的 factory 正是我們在 ViewModelProvider
初始化時润讥,默認(rèn)構(gòu)造函數(shù) defaultFactory() 方法中生成的SavedStateViewModelFactory ,所以我們直接去看這個工廠類即可盘寡。
具體源碼如下:
create(key,modelClass)
兼容舊的版本以及用戶操作行為楚殿。
相應(yīng)的,這里我們還需要再提一下竿痰,LegacySavedStateHandleController.create() 方法:
當(dāng)我們調(diào)用創(chuàng)建 ViewModel
時脆粥,內(nèi)部會調(diào)用具體的 ViewModel
工廠去創(chuàng)建,如果當(dāng)前 ViewModel
已創(chuàng)建影涉,則直接返回变隔,否則調(diào)用其 create() 方法創(chuàng)建新的 ViewModel
。在具體的創(chuàng)建方法中蟹倾,需要判斷當(dāng)前構(gòu)造函數(shù)是不是帶 application
或者 SaveStateHandle
匣缘,從而調(diào)用合適的 newInstance()
方法,最后再將創(chuàng)建好的 ViewModel
添加到 ViewModelStore
的 緩存 中。
銷毀流程
在初始化 ViewModelProvider
時孵户,還記得我們需要傳遞的 ViewModelStoreOwner
嗎萧朝?
而這個接口正是被我們的 ComponentActivity 或者 Fragment 各自實現(xiàn),相應(yīng)的 ViewModelStore
也是存在于我們的 ComponentActivity 中夏哭,所以我們直接去看示例代碼即可:
以ComponentActivity為例检柬,具體的源碼如下:
如上所示:在初始化Activity時,內(nèi)部會使用 lifecycle
添加一個生命周期觀察者竖配,并監(jiān)聽 onDestory() 通知(Act銷毀)何址,如果當(dāng)前銷毀的原因非配置更改導(dǎo)致,則調(diào)用 ViewModeltore.clear() 进胯,即清空我們的ViewModel緩存列表用爪,從而這也是為什么 ViewModel
不支持非配置更改的實例保存。
你可能會驚訝胁镐,那還怎么借助SavedStateHandle保存狀態(tài)偎血,viewModel已經(jīng)被清空了啊???
如果你記得 Activity
傳統(tǒng)處理狀態(tài)的方式,此時也就能理解為什么了盯漂?因為源頭都是一個地方颇玷,而 SavedStateHandle 僅僅只是一個更簡便的封裝而已。不過關(guān)于這個問題具體解析就缆,我們將在下面繼續(xù)進行探討帖渠,從而理解 SavedStateHandle 的完整流程。
SavedStateHandle流程解析
關(guān)于 SavedStateHandle
的使用方法我們在上面已經(jīng)敘述過了竭宰,其相關(guān)的 api 使用源碼也不是我們所關(guān)注的重點空郊,因為并不復(fù)雜,而我們主要要探討的是其整個流程切揭。
要摸清 SavedStateHandle
的流程狞甚,無非就兩個方向,即 從何而來 廓旬,又 在哪里進行使用 ??入愧。
在上面探索 ViewModel
創(chuàng)建流程時,我們發(fā)現(xiàn)嗤谚,在 get(ViewModel:xx) 方法內(nèi)部,最終的 create() 方法里,存在兩個分支:
- 存在附加參數(shù)extras(viewModel2.5.0新增);
- 不存在附加參數(shù)extras(兼容歷史版本或者用戶自定義的行為);
相應(yīng)的怔蚌,如果 ViewModel
的構(gòu)造函數(shù)中存在 SavedStateHandle 巩步,則各自的流程如下所示:
- CreationExtras.createSavedStateHandle() ;
- LegacySavedStateHandleController.create(xx).handle 桦踊;
前者使用了 CreationExtras 的擴展函數(shù) createSavedStateHandle()
:
而后者使用了 LegacySavedStateHandleController 控制器去創(chuàng)建:
總結(jié):
上述流程中椅野,兩者大致是一樣的,都需要先調(diào)用 consumeRestoredStateForKey(key)
拿到要還原的 Bundle , 再調(diào)用 SavedStateHandle.createHandle()
去創(chuàng)建 SavedStateHandle
。
那 SavedStateRegistry 又是什么呢竟闪?
我們的插入點也就在于此開始离福。
我們暫時先不關(guān)注如何還原狀態(tài),而是先搞清楚 SavedStateRegistry
是什么炼蛤,它又是從哪來而傳遞來的妖爷。然后再來看 狀態(tài)如何被還原,以及 SavedStateHandle
的創(chuàng)建流程理朋,最后再搞清與 SavedStateRegistry
又是如何進行關(guān)聯(lián)絮识。
SavedStateRegistry
其是一個用于保存狀態(tài)的注冊表,往往由 SavedStateRegistryOwner 接口所提供實現(xiàn)嗽上,從而以便與擁有生命周期的組件相關(guān)聯(lián)次舌。
比如我們常用的 ComponentActivity
或者 Fragment
默認(rèn)都實現(xiàn)了該接口。
源碼如下所示:
分析上面的代碼不難發(fā)現(xiàn)兽愤,SavedStateRegistry
本身提供了狀態(tài) 還原 與 保存 的具體能力彼念,并使用一個 map 保存當(dāng)前所有的狀態(tài)提供者,具體的狀態(tài)提供者由 SavedStateProvider 接口實現(xiàn)浅萧。
SavedStateRegistryOwner
相當(dāng)于是擁有 SavedStateRegistry
的具體類逐沙,因為本身繼承了 LifecycleOwner
接口,故其也具備 生命感知 能力惯殊,如下所示:
interface SavedStateRegistryOwner : LifecycleOwner {
val savedStateRegistry: SavedStateRegistry
}
以 ComponentActivity
為例酱吝,我們會發(fā)現(xiàn),ComponentActivity
默認(rèn)實現(xiàn) SavedStateRegistryOwner 接口土思。即 SavedStateRegistry
的創(chuàng)造以及狀態(tài)的保存务热,肯定也是 經(jīng)過我們Activity轉(zhuǎn)發(fā)處理(不然它自己怎么處理呢??)。
而在上面探索 ViewModel 初始化時己儒,我們了解到崎岂,ComponentActivity
默認(rèn)實現(xiàn)了 HasDefaultViewModelProviderFactory
接口,用于創(chuàng)建ViewModel工廠 闪湾。相應(yīng)的冲甘,其接口方法 getDefaultViewModelProviderFactory()
默認(rèn)返回的是 SavedStateViewModelFactory
,即支持狀態(tài)保存的ViewModel工廠。而該工廠構(gòu)造函數(shù)中正是需要接受一個 SavedStateRegistry
變量途样,也正是我們 ComponentActivity
中默認(rèn)保存的實例江醇,所以也不難猜測 ViewModel工廠 是如何與 SavedStateRegistry
如何關(guān)聯(lián)的。
以 ComponentActivity
的實現(xiàn)為例何暇,源碼如下:
ComponentActivity
初始化時陶夜,會創(chuàng)建一個 用于保存狀態(tài)注冊表的控制器 SavedStateRegistryController
對象,見面知意裆站,不難猜出条辟,其是用于控制 SavedStateRegistry
的具體類黔夭。并且該控制器對象會在 onCreate() 中調(diào)用 performRestore() 還原狀態(tài),并在onSaveInstanceState() 中去保存狀態(tài)羽嫡,此時也就解釋了為什么 SavedStateRegistry
能做到狀態(tài)保存本姥。
相應(yīng)的,我們還是要再去看看 SavedStateRegistryController 杭棵,以便更好的理解婚惫。
SavedStateRegistryController
用于控制 SavedStateRegistry
,對外提供了 初始化 ,狀態(tài) 還原颜屠、保存 等方法辰妙,如下所示:
簡而言之,其主要用于輔助 SavedStateRegistry 進行狀態(tài)保存與還原甫窟。
小結(jié)
我們再回顧一下上面的步驟密浑,在只關(guān)心 SavedStateHandle 如何被創(chuàng)建這樣一個大背景下,我們大致可以梳理出這樣的流程:
因為我們的 ComponentActivity 或者 Fragment 默認(rèn)已經(jīng)實現(xiàn)了 SavedStateRegistryOwner
接口粗井,而且默認(rèn)是由 SavedStateRegistryController
作為 SavedStateRegistry
的具體控制尔破,因此具體的狀態(tài)保存與還原都由該控制器去操作。
當(dāng)我們的 Activity
因為異常生命周期重建時浇衬,此時會回調(diào) onSaveInstanceState() 去保存狀態(tài)懒构,此時 SavedStateRegistryController
就會調(diào)用 performSave() 去保存當(dāng)前狀態(tài)(即將我們ViewModel的狀態(tài)保存到bundle里),然后在 Activity 重建時耘擂,在 onCreate() 方法里進行還原(即從bundle里取出我們保存的狀態(tài))胆剧。
當(dāng)我們創(chuàng)建 ViewModel
時,默認(rèn)使用的 ViewModel
工廠是支持保存狀態(tài)的 SavedStateViewModelFactory
醉冤。在初始化該工廠時秩霍,需要顯式傳遞 SavedStateRegistryOwner
接口對象到該工廠中,而該工廠的構(gòu)造函數(shù)內(nèi)蚁阳,會將 SavedStateRegistry
自行保存起來铃绒。
最后,如果要創(chuàng)建的 ViewModel
需要保存狀態(tài)(即構(gòu)造函數(shù)中存在SavedStateHadnle)螺捐,則使用保存的 SavedStateRegistry
變量去獲取我們將要還原的狀態(tài)颠悬,然后再調(diào)用 SavedStateHandle.createHandle()
去創(chuàng)建具體的 SavedStateHadnle
。
由此結(jié)合 ViewModel
創(chuàng)建的流程定血,我們可以總結(jié) SavedStateRegistry
的傳遞流程偽代碼如下:
SavedStateHandle如何創(chuàng)建
在上面赔癌,我們聊完了 SavedStateRegistry
是如何被創(chuàng)建以及被傳遞給我們的 ViewModel工廠 ,而這一小節(jié)澜沟,我們將要聊聊 SavedStateHandle
如何被創(chuàng)建届榄,以及狀態(tài)是如何被還原的。
我們知道倔喂,當(dāng)創(chuàng)建 SavedStateHandle
前铝条,需要先獲取已保存的狀態(tài),也即 consumeRestoredStateForKey()
方法席噩,所以我們本章節(jié)的插入點也就是從這里開始班缰。
而與 consumeRestoredStateForKey()
關(guān)聯(lián)的類有兩個, SavedStateHandlesProvider 與 SavedStateRegistry 。
前者是
viewModel
(2.5.0) 新提供的 創(chuàng)建SavedStateHandle 的方式悼枢,后者則是用于 適配 2.5.0 之前的方式埠忘。
以 SavedStateHandlesProvider 為例,源碼如下:
當(dāng)我們調(diào)用 consumeRestoredStateForKey()
獲取具體狀態(tài)時馒索,內(nèi)部先會調(diào)用 performRestore()
從 SavedStateRegistry 獲取我們保存的狀態(tài)集莹妒,然后將其保存到 provider
中。再從這個總的 狀態(tài)bundle 中獲取我們當(dāng)前 viewModel
所對應(yīng)的狀態(tài)绰上。
相應(yīng)的旨怠,我們再去看看 SavedStateHandle.createHandle() 方法,即 SavedStateHandle
最終被怎么創(chuàng)建出來蜈块。
源碼如下:
上述的邏輯也比較簡單鉴腻,具體如源碼中所示,當(dāng)我們創(chuàng)建 SavedStateHandle 時百揭,需要先從 SavedStateRegistry 獲取我們的狀態(tài)Bundle,然后再調(diào)用 createHandle()
方法創(chuàng)建具體的 SavedStateHandle爽哎。并在其 createHandle()
內(nèi)將我們傳入的 bundle 轉(zhuǎn)為 Map 形式,從而傳入 SavedStateHandle 的構(gòu)造函數(shù)中用于初始化器一。
總結(jié)
在這一章節(jié)课锌,我們主要探討的是 SavedStateHandle
的創(chuàng)建流程,以 ComponentActivity
為例:
我們知道 Android 中關(guān)于狀態(tài)的保存與還原祈秕,官方建議使用 onSaveInstanceState() 與 onRestoreInstanceState() 渺贤,但隨著JetPack組件庫的完善,官方在這兩個方法的基礎(chǔ)上新增了 SavedState
,目的是簡化狀態(tài)保存的成本踢步。從原理上癣亚,其創(chuàng)建了一個 狀態(tài)保存的的注冊表 SavedStateRegistry
,內(nèi)部緩存著具體的 狀態(tài)提供者合集(key為string,value為SavedStateProvider)获印。
當(dāng)我們 Activity 因為配置更改或者不可控原因需要重建時,系統(tǒng)此時會主動調(diào)用 onSaveInstanceState() 方法兼丰,從而觸發(fā)調(diào)用 savedStateRegistry.performSave()
去保存狀態(tài)玻孟。該方法內(nèi)部會創(chuàng)建一個新的 Bundle 對象,用于保存所有狀態(tài),然后再調(diào)用所有緩存的狀態(tài)提供者(SavedStateProvider)的 saveState()
方法鳍征,從而將所有需要需要保存的狀態(tài)以 key-value 的方式存到 Bundle 中去黍翎。最后再將這個整體的 bundle 存入 onSaveInstanceState()
方法參數(shù)提供的 bundle 中。
當(dāng)我們的 Activity 重建完成后艳丛,在 onCreate()
方法中匣掸,再使用 SavedStateRegistry
還原我們自己保存的狀態(tài) restoredState趟紊。
最后當(dāng)我們創(chuàng)建 ViewModel
時,因為我們的 ViewModel工廠(SavedStateViewModelFactory) 持有了 SavedStateRegistry
碰酝,也即持有著我們要還原的狀態(tài)(如果有)霎匈。在創(chuàng)建具體的 ViewModel
時,如果我們要創(chuàng)建的 ViewModel
構(gòu)造函數(shù)中存在 SavedStateHandle
參數(shù)送爸,則該 ViewModel
支持保存狀態(tài)铛嘱,所以需要先去使用 SavedStateRegistry
獲取我們保存的狀態(tài),最后再調(diào)用 SavedStateHandle.create() 去創(chuàng)建具體 SaveStateHandle
袭厂,從而創(chuàng)建出支持保存狀態(tài) ViewModel
墨吓。
結(jié)語
在本篇中,我們從 ViewModel
的背景開始纹磺,再到 ViewModel
與 SavedStateHandle
的使用方式帖烘,最后又從源碼層級分析了兩者的具體流程,從而較完整的解析了 ViewModel
的底層實現(xiàn)與 SavedStateHandle
的整體創(chuàng)建流程爽航。
至于更加詳細的使用方式蚓让,這也非本篇要深入探索的細節(jié),具體可參照其他同學(xué)的教程即可讥珍。至此历极,關(guān)于 ViewModel
設(shè)計思想 以及 狀態(tài)保存原理 ,相信讀過本篇的你也將不會有所疑問衷佃。
參閱
更多
這是 JetPack
組件系列 - ViewModel 篇氏义,如果你覺得這個系列寫的還不錯锄列,也可以看看其他篇:
作者:Petterp
鏈接:https://juejin.cn/post/7186680109384859706