網站首頁 編程語言 正文
背景
在我們android開發中,如果需要actiivty/fragment等有狀態的控件保存當前狀態,由系統進行數據保存的恢復的時候
比如正常的暗黑模式切換/后臺時低內存系統回收等等,都需要我們對當前的用戶數據進行保存,不然下次重新恢復的時候,就會出現丟失數據的情況,給用戶造成不太好的體驗
一般都會重寫onSaveInstanceState方法進行,在里面的Bundle對象進行數據的寫入,然后會在onCreate階段或者onRestoreInstanceState階段進行數據的恢復!這套生命周期框架一直是深深嵌入在我們的開發習慣中!
但是隨著項目的迭代,也隨著業務的發展,我們可以發現,在頁面復雜度提高的同時,在onSaveInstanceState里面需要保存的數據也是越來越多這就帶來了幾個問題,僅舉例
- onSaveInstanceState存儲數據復雜,可能多人迭代就存在重復存取的現象,造成代碼結構問題與隱藏bug的風險
- 不可復用,比如activity1需要存儲的數據,剛好activity2也需要存儲,這個時候就只能寫兩份代碼,以此類推
- 沒有統一的管理層,即數據的維護可能需要團隊的代碼規范
SavedState的登場
為了解決這些歷史android的設計問題,也為了更方便廣大開發者進行更好的代碼結構解耦設計,所以google大哥在jetpack庫中,推出了SavedState,它的定位是在Android開發中,編寫可插入組件,以在進程終止時保存界面狀態,并在進程重啟時恢復界面狀態。
雖然Savedstate推出來一段時間了,但是卻一直處在“默默無聞”的狀態,可能的原因有很多,比如日常開發很少接觸狀態保存,大部分app中真正需要保存狀態的其實是很少一部分,很多app甚至是不寫狀態保存邏輯的,被系統回收就回收掉了,重建就是了(即使丟失了)。
還有就是學習資料比較少,現在基本找不到SavedState的相關資料(區別于我們viewmodel常用的SavesStateHandle)。
官方的demo例子也沒有,所以Savedstate很長一段時間都是處于雪藏的狀態。但是!為了讓我們的app擁有更加好的體驗,同時也提高我們的技術視野,學習Savedstate還是很有必要的,不然官方也不會白白將其加入jetpack系列。
理解SavedState
用法
深入理解之前呢,我們必須要會用不是嘛!我們下面來看一下例子
在Activity onCreate方法中執行以下代碼
注意:savedStateRegistry.isRestored在onCreate之后就會變成true,
也就是說,我們必須在ComponentActivity的onCreate走完之后才能用,
原理看下邊的解析
if(savedStateRegistry.isRestored){
val consumeRestoredStateForKey = savedStateRegistry.consumeRestoredStateForKey("test")
val test = consumeRestoredStateForKey?.getInt("test")
Log.i("hello","tag test is $test")
}
savedStateRegistry.registerSavedStateProvider("test",DataProvider())
class DataProvider: SavedStateRegistry.SavedStateProvider {
override fun saveState(): Bundle {
return Bundle().apply {
this.putInt("test",1)
}
}
}
如果對上訴程序不理解,不要緊,我們會接下來講解!運行上訴程序,在app一開始啟動的時候,log輸出就是null,這個時候我們退到后臺(可以設置不保留活動),再次打開的時候,可以看到activity被回收重建,這個時候log輸出的卻是1,這里就驗證了SavedState具有保存數據的能力。
在demo中savedStateRegistry其實是ComponentActivity的一個對象,我們接下里分析一下,SavedState的概念組成
SavedState組成概念
SavedState庫中,有以下幾個概念
SavedStateRegistryOwner | SavedStateRegistryController | SavedStateRegistry | SavedStateProvider |
---|---|---|---|
保存狀態擁有者,用于提供聲明周期的同步操作 | 控制器,相當于一個連接角色,用于連接SavedStateRegistryOwner與SavedStateRegistry | 數據管理者,用于提供對存儲數據的相關操作 | 數據提供者,用于提供存儲的數據 |
我們再來看一下依賴關系,可以看到這個是單向依賴
看到這里,我們就對SavedState有了個初步的認識,這個時候就可以再回到demo了,首先我們的Activity是繼承了ComponentActivity,而ComponentActivity其實是實現了SavedStateRegistryOwner接口的
所以我們的數據存儲過程發起者都是由該activity的生命周期決定,而ComponentActivity里面擁有一個SavedStateRegistryController對象
?
我們可以通過SavedStateRegistryController對象,獲取到SavedStateRegistry
構成已經很清楚了,我們再來解釋一下上面的用法,其實分為兩步:
- savedStateRegistry.registerSavedStateProvider,通過savedStateRegistry的registerSavedStateProvider方法注冊一個數據保存集合,第一個參數就是自定義的key,第二個參數為實現SavedStateProvider接口的類,即DataProvider
- savedStateRegistry.consumeRestoredStateForKey可以獲取當前的存儲的數據集合,參數為我們自定義的key,如果當前存在key對應的數據,就返回具體存儲的數據(發生在重組時),如果不存在就返回null(發生在首次進入的時候)。值得注意的一個點是,如果我們沒有調用unregisterSavedStateProvider(刪除對應key的存儲數據)方法,那么除了首次進入之外,每次系統回收都會幫我們恢復在registerSavedStateProvider創建時的數據集合。
到這里,用法其實就很明確了,通過以上的分層,我們就可以把原本耦合在onSaveInstanceState的數據存儲邏輯,變成了一個個SavedStateProvider,方便了后期數據的管理與復用,即像插件一樣我們隨時可以替換具體的邏輯而不影響業務本身。
原理探究
我們來看一下registerSavedStateProvider究竟做了些什么
@MainThread
public void registerSavedStateProvider(@NonNull String key,
@NonNull SavedStateProvider provider) {
SavedStateProvider previous = mComponents.putIfAbsent(key, provider);
if (previous != null) {
throw new IllegalArgumentException("SavedStateProvider with the given key is"
+ " already registered");
}
}
private SafeIterableMap<String, SavedStateProvider> mComponents =
new SafeIterableMap<>();
可以看到,其實就是在mComponents里面放了一個SavedStateProvider對象,當前,如果我們之前存放過了,再次存放就會拋出異常,而mComponents,其實就是一個map對象。
那么我們SavedStateRegistry什么時候才觸發這個存儲邏輯呢?其實在performSave方法里面
@MainThread
void performSave(@NonNull Bundle outBundle) {
Bundle components = new Bundle();
if (mRestoredState != null) {
components.putAll(mRestoredState);
}
for (Iterator<Map.Entry<String, SavedStateProvider>> it =
mComponents.iteratorWithAdditions(); it.hasNext(); ) {
Map.Entry<String, SavedStateProvider> entry1 = it.next();
// 觸發了SavedStateProvider的saveState方法
components.putBundle(entry1.getKey(), entry1.getValue().saveState());
}
outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
}
這里有個有趣的點,它接受一個外來的Bundle,在外來的Bundle里面,再次存了一個子Bundle,而這個子Bundle,其實就是我們上文的mComponents的數據,而對應的key是一個常量
private static final String SAVED_COMPONENTS_KEY =
"androidx.lifecycle.BundlableSavedStateRegistry.key";
存放的子Bundle通過遍歷的方式,觸發了SavedStateProvider的saveState方法,獲取到我們實際上想要保存的數據。 那么是誰調用了SavedStateRegistry的performSave方法呢?當然就是 SavedStateRegistryController啦,我們說過它其實是個中間角色,大部分操作需要經過它才能調用到SavedStateRegistry
@MainThread
public void performSave(@NonNull Bundle outBundle) {
mRegistry.performSave(outBundle);
}
而SavedStateRegistryController的performSave方法,真的被調用起來,就是在ComponenntActivity中
@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
Lifecycle lifecycle = getLifecycle();
if (lifecycle instanceof LifecycleRegistry) {
((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
}
super.onSaveInstanceState(outState);
被調用
mSavedStateRegistryController.performSave(outState);
mActivityResultRegistry.onSaveInstanceState(outState);
}
到這里我們應該豁然開朗了,其實還是換湯不換藥,都是在onSaveInstanceState里面進行的邏輯保存,只不過這一層被SavedState給封裝起來了,同時加入到了androidx中,也算是官方想要重新完善onSaveInstanceState架構的體現。
看到這里了,我們也就能解釋存放在SavedStateProvider的數據,其實就存放在了onSaveInstanceState的Bundle中,key為SAVED_COMPONENTS_KEY的子Bundle中。
我們再來看一下consumeRestoredStateForKey,取數據的邏輯
@MainThread
@Nullable
public Bundle consumeRestoredStateForKey(@NonNull String key) {
if (!mRestored) {
throw new IllegalStateException("You can consumeRestoredStateForKey "
+ "only after super.onCreate of corresponding component");
}
if (mRestoredState != null) {
Bundle result = mRestoredState.getBundle(key);
mRestoredState.remove(key);
if (mRestoredState.isEmpty()) {
mRestoredState = null;
}
return result;
}
return null;
}
可以看到,只有mRestored為true的時候,我們才能真正進入到取數的邏輯,否則拋出異常,因為我們能夠獲取到數據的前提是,系統幫我們把數據進行恢復之后!而mRestored被賦值的時候,其實就在performRestore中
@SuppressWarnings("WeakerAccess")
@MainThread
void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
if (mRestored) {
throw new IllegalStateException("SavedStateRegistry was already restored.");
}
if (savedState != null) {
mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
}
....
mRestored = true;
}
有了上面存數據的邏輯,我們很容易知道,performRestore的調用者,最終肯定是由實現了 SavedStateRegistryOwner接口的ComponentActivity發起調用
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// Restore the Saved State first so that it is available to
// OnContextAvailableListener instances
//這里就是恢復數據來源
mSavedStateRegistryController.performRestore(savedInstanceState);
mContextAwareHelper.dispatchOnContextAvailable(this);
super.onCreate(savedInstanceState);
mActivityResultRegistry.onRestoreInstanceState(savedInstanceState);
ReportFragment.injectIfNeededIn(this);
if (mContentLayoutId != 0) {
setContentView(mContentLayoutId);
}
}
最后我們再給出具體的流程圖,存數據和取數據的邏輯:
最后
原文鏈接:https://juejin.cn/post/7123416990768693256
相關推薦
- 2022-07-11 在web頁面播放rtsp流視頻(webrtc)
- 2023-01-13 C語言實現繪制貝塞爾曲線的函數_C 語言
- 2022-11-22 python3.6.4安裝opencv3.4.2的實現_python
- 2022-12-29 Kotlin面向對象知識點講解_Android
- 2022-03-27 帶你從內存的角度看Python中的變量_python
- 2023-10-31 WebSocket消息推送
- 2022-03-25 C語言中字符型數據和浮點型數據介紹_C 語言
- 2022-09-15 C#獲取文件名和文件路徑的兩種實現方式_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支