1、onSaveInstanceState前端
1. 代碼示例: java
當屏幕的方向發生了改變, Activity會被摧毀而且被從新建立,若是你想在Activity被摧毀前緩存一些數據,而且在Activity被從新建立後恢復緩存的數據。能夠重寫Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法,以下代碼所示:android
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(savedInstanceState != null){ boolean myBoolean = savedInstanceState.getBoolean("MyBoolean"); double myDouble = savedInstanceState.getDouble("myDouble"); String myString = savedInstanceState.getString("myString"); Log.i("MainActivity","onCreate() :" + myString ); } } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); boolean myBoolean = savedInstanceState.getBoolean("MyBoolean"); double myDouble = savedInstanceState.getDouble("myDouble"); String myString = savedInstanceState.getString("myString"); Log.i("MainActivity","onRestoreInstanceState() :" + myString ); } @Override protected void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putBoolean("MyBoolean", true); savedInstanceState.putDouble("myDouble", 1.9); savedInstanceState.putString("myString", "abc"); super.onSaveInstanceState(savedInstanceState); Log.i("MainActivity","onSaveInstanceState() : save date"); } }
還有須要注意的是, onSaveInstanceState()方法並非必定會被調用的, 由於有些場景是不須要保存狀態數據的。 好比用戶按下BACK鍵退出activity時, 用戶顯然想要關閉這個activity, 此時是沒有必要保存數據以供下次恢復的, 也就是onSaveInstanceState()方法不會被調用。若是調用onSaveInstanceState()方法,調用將發生在onPause()或onStop()方法以前。數據庫
運行結果 : 在運行的過程當中 旋轉屏幕緩存
onSaveInstanceState() : save date onCreate() : abc
2. 基本做用: app
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()並非生命週期方法,它們不一樣於 onCreate()、onPause()等生命週期方法,它們並不必定會被觸發。當應用遇到意外狀況(如:內存不足、用戶直接按Home鍵)由系統銷燬一個Activity時,onSaveInstanceState() 會被調用。可是當用戶主動去銷燬一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被調用。由於在這種狀況下,用戶的行爲決定了不須要保存Activity的狀態。一般onSaveInstanceState()只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。
異步
在activity被殺掉以前調用保存每一個實例的狀態,以保證該狀態能夠在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (傳入的Bundle參數是由onSaveInstanceState封裝好的)中恢復。這個方法在一個activity被殺死前調用,當該activity在未來某個時刻回來時能夠恢復其先前狀態。 socket
例如,若是activity B啓用後位於activity A的前端,在某個時刻activity A由於系統回收資源的問題要被殺掉,A經過onSaveInstanceState將有機會保存其用戶界面狀態,使得未來用戶返回到activity A時能經過onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢復界面的狀態。async
關於onSaveInstanceState (),是在函數裏面保存一些View有用的數據到一個Parcelable對象並返回。在Activity的onSaveInstanceState(Bundle outState)中調用View的onSaveInstanceState (),返回Parcelable對象,ide
接着用Bundle的putParcelable方法保存在Bundle savedInstanceState中。
當系統調用Activity的的onRestoreInstanceState(Bundle savedInstanceState)時, 同過Bundle的getParcelable方法獲得Parcelable對象,而後把該Parcelable對象傳給View的onRestoreInstanceState (Parcelable state)。在的View的onRestoreInstanceState中從Parcelable讀取保存的數據以便View使用。
這就是onSaveInstanceState() 和 onRestoreInstanceState() 兩個函數的基本做用和用法。
3. 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()前)
4. onRestoreInstanceState()何時調用
onRestoreInstanceState()被調用的前提是,activity A「確實」被系統銷燬了,而若是僅僅是停留在有這種可能性的狀況下,則該方法不會被調用,例如,當正在顯示activity A的時候,用戶按下HOME鍵回到主界面,而後用戶緊接着又返回到activity A,這種狀況下activity A通常不會由於內存的緣由被系統銷燬,故activity A的onRestoreInstanceState方法不會被執行 此也說明上兩者,大多數狀況下不成對被使用。
onRestoreInstanceState()在onStart() 和 onPostCreate(Bundle)之間調用。
5. 是否須要重寫onSaveInstanceState()方法
既然該方法的默認實現能夠自動的保存UI控件的狀態數據, 那何時須要覆寫該方法呢?
若是須要保存額外的數據時, 就須要覆寫onSaveInstanceState()方法。你們須要注意的是:onSaveInstanceState()方法只適合保存瞬態數據, 好比UI控件的狀態, 成員變量的值等,而不該該用來保存持久化數據,持久化數據應該當用戶離開當前的 activity時,在 onPause() 中保存(好比將數據保存到數據庫或文件中)。說到這裏,還要說一點的就是在onPause()中不適合用來保存比較費時的數據,因此這點要理解。
因爲onSaveInstanceState()方法方法不必定會被調用, 所以不適合在該方法中保存持久化數據, 例如向數據庫中插入記錄等. 保存持久化數據的操做應該放在onPause()中。如果永久性值,則在onPause()中保存;若大量,則另開線程吧,別阻塞UI線程。
6. 引起activity銷燬和重建的其它狀況
除了系統處於內存不足的緣由會摧毀activity以外, 某些系統設置的改變也會致使activity的摧毀和重建. 例如改變屏幕方向(見上例), 改變設備語言設定, 鍵盤彈出等。
2、onRetainNonConfigurationInstance
固然了在旋轉屏幕時咱們除了使用onSaveInstanceState ()外,還可使用onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()這兩個方法來保存切換屏幕的狀態。與onSaveInstanceState () 不一樣的是,onRetainNonConfigurationInstance()和getLastNonConfigurationInstance() 方法主要用於屏幕之間的旋轉操做時保存數據。
1.代碼示例
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 獲取上次切換屏幕保存的對象 Object obj = getLastNonConfigurationInstance(); } @Override public Object onRetainNonConfigurationInstance() { // 在這裏設置須要保存的內容,在切換時不是bundle了,咱們能夠直接經過object來代替。 return super.onRetainNonConfigurationInstance(); } }
在恢復屏幕時能夠不使用onRestoreInstanceState(),而使用getLastNonConfigurationInstance()來代替。咱們能夠直接在oncreate()方法中獲取上次保存的對象。
對於咱們的程序而言,或多或少的都須要進行Activity之間的跳轉操做,其中有一些是爲了得到系統中的資源或是一些必要信息,而通常是經過啓動Activity(經常使用startActivity()和startActivityForResult()函數)來進行操做。在這個跳轉的期間,咱們當前的Activity暫時失去了焦點,處於不可操做狀態,可在此以前先經過onSaveInstanceState()方法來保存一些暫時時的數據。當回到先前的Activity時,先前的Activity從新獲取了焦點,系統就是觸發onRestoreInstanceState()方法,可獲取失去焦點前的一些數據。onRetainNonConfigurationInstance()方法也具備相似的功能來處理這樣的數據操做。先前說過,onRetainNonConfigurationInstance()方法主要是用於屏幕的旋轉操做。
說到這裏了,可能有的人就要問了, 既然onSaveInstanceState()和onRetainNonConfigurationInstance()均可以實現保存數據的功能,若是是兩個同時使用時,執行順序是哪一個在先,哪一個在後呢?根據Android官方網站上介紹,若是兩個方法同時出現時,onSaveInstanceState()方法執行在先,而onRetainNonConfigurationInstance()方法執行在後。它們的執行順序都在onStop()和onDestroy()之間,關於這點須要咱們你們注意。
以前在其它網站上看到有的朋友說:" onSaveInstanceState()和onRetainNonConfigurationInstance()既然均可以實現保存數據的功能,並且onSaveInstanceState()相比onRetainNonConfigurationInstance()方法能夠實現更多狀況下的數據保存功能,那麼onRetainNonConfigurationInstance()豈不是多餘的嗎?"。關於這點,從設計的角度看,onRetainNonConfigurationInstance()並非多餘的函數。通常狀況下,若是咱們要保存的數據不太大,並且適合放在Bundle中,那麼使用onSaveInstanceState()是比較合適的;若是要保存的數據不適合放在Bundle中(好比: 一個socket)或是數據比較大(好比:一個Bitmap),那麼這個時間咱們就應該使用onRetainNonConfigurationInstance(),並且咱們使用onRetainNonConfigurationInstance()能夠保存任何類型的對象,像AsyncTask和SQLiteDatabse,咱們均可以進行保存。這些類型的數據可能會被一個新的Activity實例所從新使用。因此onSaveInstanceState()和onRetainNonConfigurationInstance()在咱們的程序中扮演的是不一樣的角色,須要在不一樣的時機下調用,用來處理不一樣類型的數據。
例以下面代碼所示要保存一個複雜的數據:
public class DataHolder { int a; Bitmap b; String s; } @Override public Object onRetainNonConfigurationInstance() { DataHolder dh = new DataHolder(); dh.a = a; dh.b = b; dh.s = s; return dh; }
不過呢,onRetainNonConfigurationInstance()在新版本的SDK中是一個過期的方法,咱們能夠用 setRetainInstance(boolean)來代替onRetainNonConfigurationInstance(),在舊的平臺中咱們仍然可使用onRetainNonConfigurationInstance()。若是是部分朋友不知道如何使用setRetainInstance(boolean)來保存自定義的對象數據,可使用onRetainCustomNonConfigurationInstance()來表明onRetainNonConfigurationInstance(),同時使用getLastCustomNonConfigurationInstance()代替getLastNonConfigurationInstance() 。
下面是一個使用onRetainNonConfigurationInstance() 和 getLastNonConfigurationInstance()來實現屏幕旋轉時異步下載更新進度條並保存數據的效果,代碼以下所示:
package com.rotation.demo; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.os.SystemClock; import android.util.Log; import android.view.View; import android.widget.ProgressBar; /** * Android實現屏幕旋轉異步下載效果 * @Description: Android實現屏幕旋轉異步下載效果 * @File: RotationAsyncActivity.java * @Package com.rotation.demo * @Author Hanyonglu * @Date 2012-03-28 下午08:14:57 * @Version V1.0 */ public class RotationAsyncActivity extends Activity { // 進度條 private ProgressBar progressBar=null; // 異步任務類 private RotationAsyncTask asyncTask=null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); progressBar=(ProgressBar)findViewById(R.id.progress); // 獲取對象 asyncTask=(RotationAsyncTask)getLastNonConfigurationInstance(); if (asyncTask==null) { asyncTask=new RotationAsyncTask(this); asyncTask.execute(); }else { asyncTask.attach(this); updateProgress(asyncTask.getProgress()); if (asyncTask.getProgress()>=100) { markAsDone(); } } } /** * 保存對象 */ @Override public Object onRetainNonConfigurationInstance() { asyncTask.detach(); return asyncTask; } private void updateProgress(int progress) { progressBar.setProgress(progress); } private void markAsDone() { findViewById(R.id.completed).setVisibility(View.VISIBLE); } // 異步任務類 private static class RotationAsyncTask extends AsyncTask<Void, Void, Void> { private RotationAsyncActivity activity=null; private int progress=0; /** * 默認的構造器 */ public RotationAsyncTask() { // TODO Auto-generated constructor stub } /** * 帶參構造器 * @param activity */ public RotationAsyncTask(RotationAsyncActivity activity) { attach(activity); } @Override protected Void doInBackground(Void... unused) { for (int i=0;i<20;i++) { SystemClock.sleep(500); publishProgress(); } return null; } @Override protected void onProgressUpdate(Void... unused) { if (activity==null) { Log.w("RotationAsyncActivity", "onProgressUpdate()"); }else { progress += 5; activity.updateProgress(progress); } } @Override protected void onPostExecute(Void unused) { if (activity==null) { Log.w("RotationAsyncActivity", "onPostExecute()"); }else { activity.markAsDone(); } } protected void detach() { activity = null; } protected void attach(RotationAsyncActivity activity) { this.activity = activity; } protected int getProgress() { return progress; } } }
咱們在運行示例時就會發現,不管怎樣旋轉屏幕都不會影響進度條的更新與下載,須要解釋下的是這裏我並無設置下載功能,有興趣或須要的朋友本身添加便可。實現效果圖以下所示:
以上就是在Android中關於InstanceState保存數據和恢復數據的過程,在這裏我想再重複一遍:onSaveInstanceState()和onRestoreInstanceState()機制來保存數據時,它僅在非用戶顯式的指令殺死應用程序時保存和恢復數據。咱們可使用它在咱們的程序中來保存數據,能夠做爲保存數據的一種方式,但在使用過程當中須要注意其使用原理和方法。