Android APP長期運行於後臺,重啟後如何避免異常
首先來介紹onSaveInstanceState() 和 onRestoreInstanceState() 。關於這兩個方法,一些朋友可能在Android開發過程中很少用到,但在有時候掌握其用法會幫我們起到比較好的效果。尤其是在應用程式在不知道的情況下退出後,如何實現其資料儲存的功能。先來讓我們看下這兩個方法的有什麼樣的作用。
1. 基本作用:
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()並不是生命週期方法,它們不同於 onCreate()、onPause()等生命週期方法,它們並不一定會被觸發。當應用遇到意外情況(如:記憶體不足、使用者直接按Home鍵)由系統銷燬一個Activity時,onSaveInstanceState()
會被呼叫。但是當用戶主動去銷燬一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被呼叫
在activity被殺掉之前呼叫儲存每個例項的狀態,以保證該狀態可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (傳入的Bundle引數是由onSaveInstanceState封裝好的)中恢復。這個方法在一個activity被殺死前呼叫,當該activity在將來某個時刻回來時可以恢復其先前狀態。
例如,如果activity B啟用後位於activity A的前端,在某個時刻activity A因為系統回收資源的問題要被殺掉,A通過onSaveInstanceState將有機會儲存其使用者介面狀態,使得將來使用者返回到activity A時能通過onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢復介面的狀態。
關於onSaveInstanceState (),是在函式裡面儲存一些View有用的資料到一個Parcelable物件並返回。在Activity的onSaveInstanceState(Bundle outState)中呼叫View的onSaveInstanceState (),返回Parcelable物件,
接著用Bundle的putParcelable方法儲存在Bundle savedInstanceState中。當系統呼叫Activity的的onRestoreInstanceState(Bundle savedInstanceState)時, 同過Bundle的getParcelable方法得到Parcelable物件,然後把該Parcelable物件傳給View的onRestoreInstanceState (Parcelable state)。在的View的onRestoreInstanceState中從Parcelable讀取儲存的資料以便View使用。
這就是onSaveInstanceState() 和 onRestoreInstanceState() 兩個函式的基本作用和用法。
2. onSaveInstanceState() 什麼時候呼叫
先看Application Fundamentals上的一段話:
Android calls onSaveInstanceState() before the activitybecomes vulnerable to being destroyed by the system, but does not bothercalling it when the instance is actually being destroyed by a user action (suchas pressing the BACK key).
從這句話可以知道,當某個activity變得"容易"被系統銷燬時,該activity的onSaveInstanceState()就會被執行,除非該activity是被使用者主動銷燬的,例如當用戶按BACK鍵的時候。
注意上面的雙引號,何為"容易"?意思就是說該activity還沒有被銷燬,而僅僅是一種可能性。這種可能性有哪些?通過重寫一個activity的所有生命週期的onXXX方法,包括onSaveInstanceState()和onRestoreInstanceState() 方法,我們可以清楚地知道當某個activity(假定為activity A)顯示在當前task的最上層時,其onSaveInstanceState()方法會在什麼時候被執行,有這麼幾種情況:
(1)、當用戶按下HOME鍵時。
這是顯而易見的,系統不知道你按下HOME後要執行多少其他的程式,自然也不知道activity A是否會被銷燬,因此係統會呼叫onSaveInstanceState(),讓使用者有機會儲存某些非永久性的資料。以下幾種情況的分析都遵循該原則
(2)、長按HOME鍵,選擇執行其他的程式時。
(3)、按下電源按鍵(關閉螢幕顯示)時。
(4)、從activity A中啟動一個新的activity時。
(5)、螢幕方向切換時,例如從豎屏切換到橫屏時。
在螢幕切換之前,系統會銷燬activity A,在螢幕切換之後系統又會自動地建立activity A,所以onSaveInstanceState()一定會被執行,且也一定會執行onRestoreInstanceState()。
總而言之,onSaveInstanceState()的呼叫遵循一個重要原則,即當系統存在“未經你許可”時銷燬了我們的activity的可能時,則onSaveInstanceState()會被系統呼叫,這是系統的責任,因為它必須要提供一個機會讓你儲存你的資料(當然你不儲存那就隨便你了)。如果呼叫,呼叫將發生在onPause()或onStop()方法之前。(雖然測試時發現多數在onPause()前)
3. onRestoreInstanceState()什麼時候呼叫
onRestoreInstanceState()被呼叫的前提是,activity A“確實”被系統銷燬了.而如果僅僅是停留在有這種可能性的情況下,則該方法不會被呼叫,例如,當正在顯示activity A的時候,使用者按下HOME鍵回到主介面,然後使用者緊接著又返回到activity A,這種情況下activity A一般不會因為記憶體的原因被系統銷燬,故activity A的onRestoreInstanceState方法不會被執行 此也說明上二者,大多數情況下不成對被使用。
onRestoreInstanceState()在onStart() 和 onPostCreate(Bundle)之間呼叫。
4. onSaveInstanceState()方法的預設實現
如果我們沒有覆寫onSaveInstanceState()方法, 此方法的預設實現會自動儲存activity中的某些狀態資料, 比如activity中各種UI控制元件的狀態.。android應用框架中定義的幾乎所有UI控制元件都恰當的實現了onSaveInstanceState()方法,因此當activity被摧毀和重建時, 這些UI控制元件會自動儲存和恢復狀態資料. 比如EditText控制元件會自動儲存和恢復輸入的資料,而CheckBox控制元件會自動儲存和恢復選中狀態.開發者只需要為這些控制元件指定一個唯一的ID(通過設定android:id屬性即可), 剩餘的事情就可以自動完成了.如果沒有為控制元件指定ID, 則這個控制元件就不會進行自動的資料儲存和恢復操作。
由上所述, 如果我們需要覆寫onSaveInstanceState()方法, 一般會在第一行程式碼中呼叫該方法的預設實現:super.onSaveInstanceState(outState)。
5. 是否需要重寫onSaveInstanceState()方法
既然該方法的預設實現可以自動的儲存UI控制元件的狀態資料, 那什麼時候需要覆寫該方法呢?
如果需要儲存額外的資料時, 就需要覆寫onSaveInstanceState()方法。大家需要注意的是:onSaveInstanceState()方法只適合儲存瞬態資料, 比如UI控制元件的狀態, 成員變數的值等,而不應該用來儲存持久化資料,持久化資料應該當使用者離開當前的 activity時,在 onPause() 中儲存(比如將資料儲存到資料庫或檔案中)。說到這裡,還要說一點的就是在onPause()中不適合用來儲存比較費時的資料,所以這點要理解。
由於onSaveInstanceState()方法方法不一定會被呼叫, 因此不適合在該方法中儲存持久化資料, 例如向資料庫中插入記錄等. 儲存持久化資料的操作應該放在onPause()中。若是永久性值,則在onPause()中儲存;若大量,則另開執行緒吧,別阻塞UI執行緒。
6. 引發activity銷燬和重建的其它情況除了系統處於記憶體不足的原因會摧毀activity之外, 某些系統設定的改變也會導致activity的摧毀和重建. 例如改變螢幕方向(見上例), 改變裝置語言設定, 鍵盤彈出等。
另外,當螢幕的方向發生了改變, Activity會被摧毀並且被重新建立,如果你想在Activity被摧毀前快取一些資料,並且在Activity被重新建立後恢復快取的資料。可以重寫Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法。
下面來一個關於橫豎屏切換儲存資料的例子
- private VideoView videoView;
- privatestaticfinal String VIDEO_PATH = Environment
- .getExternalStorageDirectory()
- + File.separator
- + "mymovie"
- + File.separator + "shenghuaweiji.mp4";
- /** Called when the activity is first created. */
- @Override
- publicvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Log.v("tag", "onCreate");
- if (videoView == null) {
- videoView = (VideoView) this.findViewById(R.id.myvideo);
- MediaController controller = new MediaController(this);
- videoView.setMediaController(controller);
- videoView.setVideoPath(VIDEO_PATH);
- videoView.requestFocus();
- }
- if (savedInstanceState != null
- && savedInstanceState.getInt("currentposition") != 0) {
- videoView.seekTo(savedInstanceState.getInt("currentposition"));
- }
- videoView.start();
- }
onCreate方法中的引數savedInstanceState就是儲存的Activity一些狀態。
savedInstanceState.getInt("currentposition") 獲取視訊播放時間。
- @Override
- protectedvoid onSaveInstanceState(Bundle outState) {
- // TODO Auto-generated method stub
- outState.putInt("currentposition", videoView.getCurrentPosition());
- Log.v("tag", "onSaveInstanceState");
- super.onSaveInstanceState(outState);
- }
這樣在橫豎屏切換時保證了播放狀態。
參考:http://blog.csdn.net/yuzhiboyi/article/details/7677026#
http://www.cnblogs.com/hanyonglu/archive/2012/03/28/2420515.html