1. 程式人生 > >Android APP長期運行於後臺,重啟後如何避免異常

Android APP長期運行於後臺,重啟後如何避免異常

首先來介紹onSaveInstanceState() 和 onRestoreInstanceState() 。關於這兩個方法,一些朋友可能在Android開發過程中很少用到,但在有時候掌握其用法會幫我們起到比較好的效果。尤其是在應用程式在不知道的情況下退出後,如何實現其資料儲存的功能。先來讓我們看下這兩個方法的有什麼樣的作用。

  1. 基本作用: 

  Activity的 onSaveInstanceState() 和 onRestoreInstanceState()並不是生命週期方法,它們不同於 onCreate()、onPause()等生命週期方法,它們並不一定會被觸發。當應用遇到意外情況(如:記憶體不足、使用者直接按Home鍵)由系統銷燬一個Activity時,onSaveInstanceState() 會被呼叫。但是當用戶主動去銷燬一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被呼叫

。因為在這種情況下,使用者的行為決定了不需要儲存Activity的狀態。通常onSaveInstanceState()只適合用於儲存一些臨時性的狀態,而onPause()適合用於資料的持久化儲存。

  在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()方法。

下面來一個關於橫豎屏切換儲存資料的例子

  1. private VideoView videoView;    
  2. privatestaticfinal String VIDEO_PATH = Environment    
  3.         .getExternalStorageDirectory()    
  4.         + File.separator    
  5.         + "mymovie"
  6.         + File.separator + "shenghuaweiji.mp4";    
  7. /** Called when the activity is first created. */
  8. @Override
  9. publicvoid onCreate(Bundle savedInstanceState) {    
  10.     super.onCreate(savedInstanceState);    
  11.     setContentView(R.layout.main);    
  12.     Log.v("tag""onCreate");    
  13.     if (videoView == null) {    
  14.         videoView = (VideoView) this.findViewById(R.id.myvideo);    
  15.         MediaController controller = new MediaController(this);    
  16.         videoView.setMediaController(controller);    
  17.         videoView.setVideoPath(VIDEO_PATH);    
  18.         videoView.requestFocus();    
  19.     }    
  20.     if (savedInstanceState != null
  21.             && savedInstanceState.getInt("currentposition") != 0) {    
  22.         videoView.seekTo(savedInstanceState.getInt("currentposition"));    
  23.     }    
  24.     videoView.start();    
  25. }    

onCreate方法中的引數savedInstanceState就是儲存的Activity一些狀態。

savedInstanceState.getInt("currentposition")  獲取視訊播放時間。

  1. @Override
  2. protectedvoid onSaveInstanceState(Bundle outState) {    
  3.     // TODO Auto-generated method stub  
  4.     outState.putInt("currentposition", videoView.getCurrentPosition());    
  5.     Log.v("tag""onSaveInstanceState");    
  6.     super.onSaveInstanceState(outState);    
  7. }     
重寫了onSaveInstanceState方法:將當前video的播放時間儲存在Bundle中。

這樣在橫豎屏切換時保證了播放狀態。

參考:http://blog.csdn.net/yuzhiboyi/article/details/7677026#

http://www.cnblogs.com/hanyonglu/archive/2012/03/28/2420515.html