Activity/Fragment 狀態緩存和恢復的最佳實踐

The Real Best Practices to Save/Restore Activity’s and Fragment’s state
英文原文:https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en

幾個月前我發佈過一篇關於Fragment緩存和恢復狀態的文章:Probably be the best way (?) to save/restore Android Fragment’s state so far . 收到了許多來自世界各地Android開發者的寶貴建議和反饋,在此對大家說聲謝謝嗷☺。(這裏的文章原文博主已經刪除,國內有翻譯,下文中的StatedFragment就是這篇文章中的最終產物,爲的是更方便的實現Fragment狀態的緩存和恢復,不過已經不推薦大家使用)。
但是StatedFragment的策略和官方設定的緩存策略是不一樣的,因爲官方設定的策略可能讓開發者能更容易的理解Fragment的狀態緩存和恢復,使其表現的像Activity那樣能同時操控View和變量。爲此我做了個實驗來測試StatedFragment,以驗證它是否更加容易理解?它的設計對開發者來說是否更加友好?經過兩個月的測試,我想我已經得出結論了:雖然StatedFragment顯得更容易理解,但同時它也帶來了一個大問題,即它破壞了Android View的架構設計,可能在以後帶來更多問題,並且,我也開始懷疑之前寫的代碼有點詭異… 因爲這些原因,我決定從現在開始廢棄掉StatedFragment。當然,爲了擬補這個錯誤,我決定寫下這篇博客來展示下Android中Fragment狀態緩存和恢復的最佳實踐。

理解當Activity狀態被緩存或回收時發生了什麼
當Activity的onSaveInstanceState 方法被調用時,Activity將自動蒐集它所有子View的狀態,但是要注意,只有內部實現了狀態緩存和恢復接口的View才能被蒐集到!(即Activity的子View必須在內部實現狀態緩存和恢復的接口) ,隨後在onRestoreInstanceState方法被調用的時候,Activity將根據View的id(android:id)來把緩存的數據一對一的傳遞回去,如下圖所示。
這裏寫圖片描述
這裏寫圖片描述
正是因爲這個原因,EditText才能在Activity被銷燬後重建到時候仍然保持着銷燬前的狀態,即使我們啥也沒幹。這不是魔法,這些View能自動的緩存和恢復狀態。這也是爲什麼哪些沒有id(android:id)的View就不能緩存和恢復狀態的原因。
雖然這些View能自動的緩存狀態,但是對於Activity的成員變量來說就行不通了,成員變量會隨着Activity的銷燬而被回收,所以你必須通過onSaveInstanceState方法和onRestoreInstanceState方法手動地緩存和恢復它們。示例如下:
這裏寫圖片描述

理解當Fragment狀態被緩存或回收時發生了什麼
當Fragment被系統銷燬時,它的表現和Activity是一樣一樣的。如下圖所示:
這裏寫圖片描述
這裏寫圖片描述
這就意味着每一個成員變量也會被銷燬掉,所以你必須通過onSaveInstanceState方法和onActivityCreated方法來手動地緩存和恢復狀態。同志們要注意啊,Fragment裏是沒有onRestoreInstanceState方法的,這裏和Activity不一樣喲~示例如下:
這裏寫圖片描述

對於Fragment來說有些特殊情況是和Activity不一樣的,我希望大家能注意下:當Fragment從棧中重新顯示的時候,他的View將會被銷燬重建!
這裏寫圖片描述
這種情況下要知道,Fragment對象本身並沒有被銷燬,只是它包含的View會銷燬重建。 這種情況下是不會觸發緩存狀態的。那麼當這些重建的View會有什麼樣的表現呢?其實這不是啥問題,Android早就考慮過這個。這種情況下View內部的狀態緩存和恢復接口會被觸發,所以只要是實現了這些接口的View都會自動緩存和恢復它自己的狀態,例如具有android:freezeText=”true”屬性的EditTextTextView,這樣它就和銷燬前的狀態一樣了。如下圖所示:
這裏寫圖片描述
再次提醒下,這種情況下只有View會銷燬重建,Fragment對象本身一直沒變,所以它內部的成員變量也不會被銷燬,所以你不需要做額外工作。
這裏寫圖片描述

吶,現在你應該明白了,只要是實現了狀態緩存和恢復接口的View,在Fragment內部都能自動的進行狀態緩存和恢復,而不需要添加額外的工作,那麼重點來了,對於Fragment來說,緩存和恢復狀態最好的辦法就是~~~

應用中所有View都在其內部實現狀態緩存和恢復的接口
Android已經提供了給View實現狀態緩存和恢復的方法:onSaveInstanceStateonRestoreInstanceState,這是開發者要去實現的任務。
這裏寫圖片描述
總的來說,每一個系統控件,例如EditText, TextView, Checkbox等,它們都已經在內部實現了狀態緩存和恢復的接口,在使用這些特性的時候只需要設置一下相關的屬性爲true即可,例如TextView 設置android:freezeText=”true”
但是,網上衆多第三方控件呢,老實講,很多都沒有在內部實現這些接口,導致在實際使用過程中可能帶來大麻煩。
如果你決定要使用第三方控件,那麼你必須要確認它有在內部實現緩存和恢復狀態的接口,否則你需要繼承它去寫一個子控件來手動實現 onSaveInstanceState/onRestoreInstanceState .示例如下
這裏寫圖片描述
此外,別忘了當你直接繼承自View或ViewGroup的時候也要去實現這兩個方法,這真的真的真的很重要喲~
還有!別忘了給那些你希望能自動緩存和恢復狀態的View添加id!!!
這裏寫圖片描述
到此,我們已經搞定一半的工作了!!!

明確的區分開Fragment的狀態和View的狀態
爲了使你的代碼更簡潔和更具維護性,你必須區分開Fragment的狀態和View的狀態。如果一個屬性是屬於View的,那麼在View內部去緩存和恢復它,如果一個屬性是屬於Fragment的,那麼在Fragment內部是緩存和恢復它。(羅裏吧嗦的,反正就是不要混淆了View和Fragment的狀態嘛) ,示例如下:
這裏寫圖片描述
本帥再次提醒:不要在Fragment的onSaveInstanceState方法裏緩存View的狀態,反之亦然。

好了,這就是Activity/Fragment 狀態緩存和恢復的最佳實踐,希望能對你有幫助。

發佈了27 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章