Android 組件系列-----Activity保存狀態

本篇隨筆將詳細的講解Activity保存狀態的概念,也就是saving activity state。

一、Activity狀態保持概念

保存Activity的狀態是非常重要的,例如我們在玩一個遊戲的時候,突然來了一個電話,這個時候在接聽完電話之後我們返回到遊戲中,這個時候我們希望遊戲還是之前那個進度,或者說發生突發事件,遊戲這個應用程序被關閉了,這個時候我們如果再重新打開遊戲的話,我們如果還是希望回到之前的進度,我們就需要將其狀態保存起來,這樣在Activity的摧毀時,我們還能夠根據保存的狀態回到之前的進度。這就是Activity的狀態保存。

二、兩種方式的情況下Activity的狀態會被保存

Activity的狀態被保存通常有兩種方式,我們首先通過android的官方文檔提供的圖來看一下這兩種方式:

11204326-8ca7f26208aa465baf510fc73da3b3e

1.當一個Activity位於另一個Activity的前面時,也就是另一個Activity處於stop狀態,這個時候這個Activity仍然佔用着內存,並且保持着Activity的狀態,如果此時點擊後退按鈕,那麼此時第一個Activity又會重新回到前臺界面上,此時這個Activity會保持原來的狀態,我們不需要重新獲得其狀態。

2.當我們的這個Activity處於stop狀態在後臺時,如果此時有一個優先級別更高的Activity需要獲得資源,此時系統可能會破壞處於stop狀態的Activity,回收其內存,此時這個Activity對象會被destroyed,此時如果我們必須調用一個 onSaveInstanceState() 方法來保存我們的Activity的對象狀態。

onSaveInstanceState(Bundle outState)這個方法接受一個Bundle類型參數,我們可以將我們需要保存的狀態通過Bundle的 putString, putInt 方法保存起來。

當我們的Activity處於極易被摧毀的時候,系統會調用 onSaveInstanceState() 方法,如果此時系統殺死了這個Activity的線程,這個Activity對象被destroy後,再打開這個Activity時,又會重新創建這個Activity,這個時候系統會將 onSaveInstanceState 方法中的 Bundle 對象傳遞給Activity的 onCreate()和 onRestoreInstanceState() 方法,

使用這兩個方法中的任何一個,我們都可以根據之前保存的 Bundle 對象來恢復我們Activity之前的狀態。

三、onSaveInstanceState方法

protectedvoid onSaveInstanceState (Bundle outState)

下面我們具體看看這個方法,通過這個方法我們可以在一個Activity被殺死時,並在將來如果要重新創建這個Activity時可以恢復其保存的狀態。我們不需要疑惑這個方法和Activity生命週期函數方法的調用時期,例如onPause()方法,當一個Activity處於後臺時或者容易受到破壞時,這個方法就會被調用。

有兩種情況是不會調用這個onSaveInstanceState方法的:

①activity B 位於 activity A的前面,此時如果點擊 Back 按鈕,activity B 會分別調用 onPause、onStop方法,此時系統並不會調用 onSaveInstanceState() 方法,因爲此時是我們顯示的關閉activity B,所以系統認爲調用 onSaveInstanceState() 是沒有並要的。

②activity B 位於 activity A的前面,此時activity A處於後臺狀態,但是還是佔用了內存資源,當通過Back 按鈕,使得activity A重新回到前臺時,onSaveInstanceState()方法也是沒有必要調用的,因爲此時activity A本身就完整的保存了當前的狀態。


接下來我們通過一個實例來看看通過Activity的 onSaveInstanceState() 、onCreate()以及onRestoreInstanceState()方法的調用來保持我們Activity的狀態。

複製代碼
publicclass ThirdActivity extends Activity{  privatefinal String TAG = "ThirdActivity";  private Button button;  @Override  protectedvoid onCreate(Bundle savedInstanceState)  {    super.onCreate(savedInstanceState);    setContentView(R.layout.third);    Log.i(TAG, "ThirdActivity onCreate");        if(savedInstanceState != null)    {      String name = (String)savedInstanceState.getString("name");      Toast.makeText(ThirdActivity.this, "onCreate --->  " + name, 1).show();    }        button = (Button)findViewById(R.id.button);    button.setOnClickListener(new OnClickListener()    {      @Override      publicvoid onClick(View view)      {        Intent intent = new Intent();        intent.setClass(ThirdActivity.this, FourthActivity.class);        startActivity(intent);      }    });      }    @Override  protectedvoid onStart()  {    super.onStart();    Log.i(TAG, "ThirdActivity onStart");  }    @Override  protectedvoid onResume()  {    super.onResume();    Log.i(TAG, "ThirdActivity onResume");  }    @Override  protectedvoid onPause()  {    super.onPause();    Log.i(TAG, "ThirdActivity onPause");  }    @Override  protectedvoid onSaveInstanceState(Bundle outState)  {    super.onSaveInstanceState(outState);    Log.i(TAG, "ThirdActivity onSaveInstanceState");    outState.putString("name", "xiaoluo");  }    @Override  protectedvoid onRestoreInstanceState(Bundle savedInstanceState)  {    super.onRestoreInstanceState(savedInstanceState);    Log.i(TAG, "ThirdActivity onRestoreInstanceState");    if(savedInstanceState != null)    {      String name = (String)savedInstanceState.getString("name");      Toast.makeText(ThirdActivity.this, "onRestoreInstanceState ---> " + name, 1).show();    }  }    @Override  protectedvoid onStop()  {    super.onStop();    Log.i(TAG, "ThirdActivity onStop");  }    @Override  protectedvoid onRestart()  {    super.onRestart();    Log.i(TAG, "ThirdActivity onRestart");  }    @Override  protectedvoid onDestroy()  {    super.onDestroy();    Log.i(TAG, "ThirdActivity onDestroy");  }}
複製代碼

我們看到,在這個Activity中,我們實現了其 onStartInstanceState()、onCreate()和onRestoreInstanceState()方法,我們在 onStartInstanceState() 方法中將當前的Activity的狀態保存下來:

複製代碼
  @Override  protectedvoid onSaveInstanceState(Bundle outState)  {    super.onSaveInstanceState(outState);    Log.i(TAG, "ThirdActivity onSaveInstanceState");    outState.putString("name", "xiaoluo");  }  
複製代碼

然後在onCreate()方法和onRestoreInstanceState() 方法中試圖得到保存的Bundle對象,當Activity第一次被創建的時候,onCreate()和onRestoreInstanceState()方法中的Bundle對象是null的

複製代碼
protectedvoid onCreate(Bundle savedInstanceState)  {    super.onCreate(savedInstanceState);    setContentView(R.layout.third);    Log.i(TAG, "ThirdActivity onCreate");        if(savedInstanceState != null)    {      String name = (String)savedInstanceState.getString("name");      Toast.makeText(ThirdActivity.this, "onCreate --->  " + name, 1).show();    }        button = (Button)findViewById(R.id.button);    button.setOnClickListener(new OnClickListener()    {      @Override      publicvoid onClick(View view)      {        Intent intent = new Intent();        intent.setClass(ThirdActivity.this, FourthActivity.class);        startActivity(intent);      }    });      }
複製代碼
複製代碼
@Override  protectedvoid onRestoreInstanceState(Bundle savedInstanceState)  {    super.onRestoreInstanceState(savedInstanceState);    Log.i(TAG, "ThirdActivity onRestoreInstanceState");    if(savedInstanceState != null)    {      String name = (String)savedInstanceState.getString("name");      Toast.makeText(ThirdActivity.this, "onRestoreInstanceState ---> " + name, 1).show();    }  }
複製代碼

我們在這兩個方法裏分別使用 Toast 的彈出框來看看是否能將Bundle保存的狀態值打印出來。我們爲了模擬這個實驗,需要通過將手機屏幕的橫豎屏進行切換。

當屏幕的方向被改變的時候,系統會首先destroy然後recreate這個Activity對象來根據我們配置的資源文件重新加載界面,這個時候保存我們的Activity的狀態是非常重要的,因爲在大多數情況下,屏幕放心的改變是經常發生的事,所以這個時候我們必須通過 onSaveInstanceState() 方法來保存我們的Activity的狀態。

我們來看看實驗結果:

11211723-13c0c6d5927d4f6f85abe3b38f84f5911211801-bf25dda5a076463e81d353ee1657d47

11211916-e2a3c2bc1055402287adce47bea92d9

我們看到,當我們反轉屏幕的時候,因爲之前已經通過 onSaveInstanceState()方法保存了Activity的狀態,所以在Activity從destroy到recreate時,會將保存的Bundle對象傳給onCreate和onRestoreInstanceState方法,此時我們就能夠恢復我們Activity的狀態了。

四、Android View控件的onSaveInstanceState()方法

當我們在創建一個Activity對象的時候,我們如果沒有重寫父類的 onSaveInstanceState()方法,此時我們的一些Activity狀態也會通過調用父類Activity的默認的 onSaveInstanceState()方法來保存下來。特別地:父類的onSaveInstanceState()方法會調用佈局文件中每一個View對象的相應的 onSaveInstanceState()方法 來保持各自的狀態。在Android的大多數的widget控件都非常好的實現了 onSaveInstanceState()方法,因此我們對這些空間的值的改變都會被自動的保存下來。例如我們的EditText、Checkbox控件,當我們在輸入了我們的值只會,當Activity被destroy-->recreate的時候,此時我們的值仍然會被保存下來,前提是如果我們需要保存一個View控件的狀態,我們必須要給其指定一個唯一的標識符(通過 android:id 屬性來指定),如果我們沒有指定的話,系統則不會保存其狀態。例如我們來看一下下面這個例子:

複製代碼
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent"><TextView     android:id="@+id/textView1"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:textSize="20sp"    android:text="usenrame"/><EditText     android:id="@+id/username"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_toRightOf="@id/textView1"    android:inputType="text"/><TextView     android:id="@+id/textView2"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_below="@id/username"    android:textSize="20sp"    android:text="email"/><EditText     android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_below="@id/username"    android:layout_toRightOf="@id/textView2"    android:inputType="textEmailAddress"/><CheckBox     android:id="@+id/checkBox1"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_marginTop="100dp"    android:text="籃球"/><CheckBox     android:id="@+id/checkBox2"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_toRightOf="@id/checkBox1"    android:layout_alignTop="@id/checkBox1"    android:text="足球"/><CheckBox     android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_toRightOf="@id/checkBox2"    android:layout_alignTop="@id/checkBox1"    android:text="網球"/></RelativeLayout>
複製代碼

我們這個Activity的界面一共有5個View控件,其中username這個EditText我們指定了ID,email這個EditText沒有指定ID,而下面的三個CheckBox,只有最後一個CheckBox沒有爲其指定ID,我們來看看,當這個Activity被重新創建時,其會不會保存每個View控件的狀態:

11213033-1f44c73c78a1416bb4d26d7802a7864

我們在兩個文本框中輸入了值,然後將三個CheckBox都勾選上,此時我們翻轉我們的屏幕:

11213126-8cb62e7df05948958d20de234a45788

我們看到,因爲username這個EditText和前兩個CheckBox我們給其指定了ID,所以系統會調用其 onSaveInstanceState() 方法來保存我們的View控件狀態,而對於email這個EditText和最後一個CheckBox,我們沒有指定ID標識符,所以系統不會自動爲其保存狀態。

注意:儘管默認的Activity的onSaveInstanceState() 方法會保存我們的View控件的狀態,但是我們仍然推薦重新其onSaveInstanceState() 方法來保存我們額外的一些Activity的狀態,在分別重寫 onCreate()、onSaveInstanceState() 和 onRestoreInstanceState()方法時,我們要首先調用父類的方法才行,這樣就會默認的保存我們View控件的狀態了

最後再總結一句:因爲 onStartInstanceState() 方法不能保證一定會被調用,所以我們在onStartInstanceState() 方法中只能用來保存我們的Activity的臨時的狀態信息,而對於要持久化保存的對象或狀態,我們應該在 onPause() 方法中來做


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章