由淺入深烦衣,詳解 ViewModel 的那些事

引言

關(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() 方法里,存在兩個分支:

  1. 存在附加參數(shù)extras(viewModel2.5.0新增);
  2. 不存在附加參數(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)的類有兩個, SavedStateHandlesProviderSavedStateRegistry

前者是 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 的背景開始纹磺,再到 ViewModelSavedStateHandle 的使用方式帖烘,最后又從源碼層級分析了兩者的具體流程,從而較完整的解析了 ViewModel 的底層實現(xiàn)與 SavedStateHandle 的整體創(chuàng)建流程爽航。

至于更加詳細的使用方式蚓让,這也非本篇要深入探索的細節(jié),具體可參照其他同學(xué)的教程即可讥珍。至此历极,關(guān)于 ViewModel 設(shè)計思想 以及 狀態(tài)保存原理 ,相信讀過本篇的你也將不會有所疑問衷佃。

參閱

更多

這是 JetPack 組件系列 - ViewModel 篇氏义,如果你覺得這個系列寫的還不錯锄列,也可以看看其他篇:

作者:Petterp
鏈接:https://juejin.cn/post/7186680109384859706

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邻邮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子克婶,更是在濱河造成了極大的恐慌筒严,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件情萤,死亡現(xiàn)場離奇詭異鸭蛙,居然都是意外死亡,警方通過查閱死者的電腦和手機筋岛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門娶视,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人睁宰,你說我怎么就攤上這事肪获∏蘖瑁” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵贪磺,是天一觀的道長硫兰。 經(jīng)常有香客問我,道長寒锚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任违孝,我火速辦了婚禮刹前,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雌桑。我一直安慰自己喇喉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布校坑。 她就那樣靜靜地躺著拣技,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耍目。 梳的紋絲不亂的頭發(fā)上膏斤,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音邪驮,去河邊找鬼莫辨。 笑死,一個胖子當(dāng)著我的面吹牛毅访,可吹牛的內(nèi)容都是我干的沮榜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼喻粹,長吁一口氣:“原來是場噩夢啊……” “哼蟆融!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起守呜,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤型酥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后弛饭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冕末,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年侣颂,在試婚紗的時候發(fā)現(xiàn)自己被綠了档桃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡憔晒,死狀恐怖藻肄,靈堂內(nèi)的尸體忽然破棺而出蔑舞,到底是詐尸還是另有隱情,我是刑警寧澤嘹屯,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布攻询,位于F島的核電站,受9級特大地震影響州弟,放射性物質(zhì)發(fā)生泄漏钧栖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一婆翔、第九天 我趴在偏房一處隱蔽的房頂上張望拯杠。 院中可真熱鬧,春花似錦啃奴、人聲如沸潭陪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽依溯。三九已至吉拳,卻和暖如春蚁袭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗽交。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工壹粟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拜隧,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓趁仙,卻偏偏與公主長得像洪添,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子雀费,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容