Android中突發情況資料的儲存和恢復
原文地址:請掃文後stormzhang大神的微信公眾號——AndroidDeveloper
本文由我的一位讀者「MeloDev」獨家授權投稿,這個知識點堪稱面試常問的基礎知識點之一,建議多讀幾遍,一定理解掌握。微信不支援外鏈,可點選「閱讀原文」檢視。
寫在前面
在我們的APP使用的過程中,總有可能出現各種手滑、被壓在後臺、甚至突然被殺死的情況。所以對APP中一些臨時資料或關鍵持久型資料,就需要我們使用正確的方式進行儲存或恢復。
突發情況都有哪些
因為本文討論的是當一些突發情況的出現時,對資料的儲存和恢復。所以現在總結一下突發情況應該都有哪些?
-
點選back鍵
-
點選鎖屏鍵
-
點選home鍵
-
其他APP進入前臺
-
啟動了另一個Activity
-
螢幕方向旋轉
-
APP被Kill
當這些突發情況發生的時候,有哪些關鍵的方法會被呼叫呢?
寫了一個簡單的demo,我用上述的突發情況進行測試,程式碼中我重寫了所有Activity的生命週期方法和onSaveInstanceState方法,並列印對應的log在控制檯,下面是demo圖:
這裡就不貼出測試的過程了,直接來告訴大家測試的結果吧:
當我的APP處在前臺,能與使用者互動的情況下,出現上述的突發事件時,只有點選back鍵,onSaveInstanceState方法不會呼叫。其餘的情況下, 該方法一律都會呼叫,這又是為什麼呢?並且onPause方法是必然會呼叫的,這又給我們儲存資料提供了怎樣的思路呢?
onSaveInstanceState
好吧,相信當你看到本文標題的時候,你就應該想到了這個方法。因為當我們學習Android基礎知識時,用onSaveInstanceState方法進行資料恢復是你必然學到過的。所以前面我營造出的一些懸念看似是失敗了,不過對於onSaveInstanceState你理應知道更多知識:
1. 何時呼叫:
Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key)
找到了以上一段話,翻譯過來就是當某個activity變得“容易”被系統銷燬時,該activity的onSaveInstanceState就會被執行,除非該activity是被使用者主動銷燬的,例如當用戶按BACK鍵的時候。
結合我們以上的例子,其實都在說明一個詞,就是被動。當Activity並不是由我主動點選back鍵而喪失焦點時,onSaveInstanceState方法就一定會呼叫。就例如我上述列舉的那些除了點選back鍵的「突發情況」。
2. 何地呼叫:
在我寫的這個demo中,onSaveInstanceState的呼叫是處於onPause和onStop之間的,(下面關於Activity的生命週期方法,會講解一些值得大家注意的),我查閱了一下資料,能保證的是onSaveInstanceState方法會在onStop之前呼叫,但是是否在onPause之前就不一定了。
結論: google工程師們對onSaveInstanceState如此設計就是讓其完成對一些臨時的、非永久資料儲存並進行恢復。什麼樣的資料屬於臨時資料呢?舉個例子,比如EditText中輸入的內容,CheckBox是否勾選,ScrollView的滑動位置,目前視訊的播放位置等等。
當我還沒有自學Android時,玩著一些APP就會產生一個疑問,比如我在一個輸入框中輸入了大量文字沒有提交或者儲存。此時來了一個電話,如果退回的時候,輸入框裡面的文字消失了,那我可能會砸了電話,所以這個儲存資料的操作,是Android開發者做的嗎?
然而是不需要的,因為Android的View本身自己就實現了onSaveInstanceState方法,這些控制元件自己就具有儲存臨時資料和恢復臨時資料的能力。
例如TextView中的部分原始碼:
其他View控制元件都有相似的實現原理。值得一提的是,只有當你給這個wiget在xml中指定id時,它才具有儲存資料並且恢復的能力,並且不同的wiget還不能共用這個id,否則會出現資料覆蓋的情況。具體的原始碼有興趣大家可以自己去看,這裡因為篇幅的原因不再貼出,關於onSaveInstanceState我們先說這些,趕緊看看使用姿勢。
onSaveInstanceState的使用姿勢
比如我們要儲存當前視訊的播放進度,這個顯然控制元件沒有幫我們實現onSaveInstanceState,所以就只能靠自己了,程式碼如下所示。
當在onCreate取出臨時資料時,記得加一個非空判斷。
看到這裡,也許你認為本文該就此結束了,不過在回過頭看看,我們剛才一直強調的是臨時資料,畢竟onSaveInstanceState本身就是為臨時資料服務的,但是一些永久性質的資料,比如插入資料庫的操作,我們應該在什麼方法中對其進行儲存呢?
onPause
在介紹onPause方法之前,還是想聊聊Activity的生命週期方法,相信大家對它應該有了初步的瞭解,不過在相應的生命週期方法中,我們應該做什麼操作呢?推薦給大家一篇文章,我覺得不錯。
關於onPause,我找到了一下關於它的特性:
onPause(), onStop(), onDestroy() are "killable after" lifecycle methods. This indicates whether or not the system can kill the process hosting the activity at any time after the method returns, without executing another line of the activity's code. Because onPause() is the first of the three, once the activity is created, onPause() is the last method that's guaranteed to be called before the process can be killed—if the system must recover memory in an emergency, then onStop() and onDestroy() might not be called. Therefore, you should use onPause() to write crucial persistent data (such as user edits) to storage. However, you should be selective about what information must be retained during onPause(), because any blocking procedures in this method block the transition to the next activity and slow the user experience.
翻譯過來就是:無論出現怎樣的情況,比如程式突然死亡了,能保證的就是onPause方法是一定會呼叫的,而onStop和onDestory方法並不一定,所以這個特性使得onPause是持久化相關資料的最後的可靠時機。當然onPause方法不能做大量的操作,這會影響下一個Activity入棧。
剛才我們的測試結果還說明了一個道理,onSaveInstanceState並不是百分百呼叫的(比如點選了back鍵),顯然一些永久性的資料,我們並不能在此中儲存。
關於本文的結論就顯而易見了,我們來一句話總結一下:
臨時資料使用 onSaveInstanceState 儲存恢復,永久性資料使用 onPause 方法儲存。
微信不支援外鏈,可點選「閱讀原文」檢視,覺得有幫助不妨轉發支援下,長按二維碼訂閱。