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

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

1、Activity狀態保持概念ide

保存Activity的狀態是很是重要的,例如咱們在玩一個遊戲的時候,忽然來了一個電話,這個時候在接聽完電話以後咱們返回到遊戲中,這個時候咱們但願遊戲仍是以前那個進度,或者說發生突發事件,遊戲這個應用程序被關閉了,這個時候咱們若是再從新打開遊戲的話,咱們若是仍是但願回到以前的進度,咱們就須要將其狀態保存起來,這樣在Activity的摧毀時,咱們還可以根據保存的狀態回到以前的進度。這就是Activity的狀態保存。函數

2、兩種方式的狀況下Activity的狀態會被保存佈局

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

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

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

onSaveInstanceState(Bundle outState)這個方法接受一個Bundle類型參數,咱們能夠將咱們須要保存的狀態經過Bundle的 putString, putInt 方法保存起來。code

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

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

3、onSaveInstanceState方法

protected void 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的狀態。

public class ThirdActivity extends Activity
{
    private final String TAG = "ThirdActivity";
    private Button button;
    @Override
    protected void 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
            public void onClick(View view)
            {
                Intent intent = new Intent();
                intent.setClass(ThirdActivity.this, FourthActivity.class);
                startActivity(intent);
            }
        });
        
    }
    
    @Override
    protected void onStart()
    {
        super.onStart();
        Log.i(TAG, "ThirdActivity onStart");
    }
    
    @Override
    protected void onResume()
    {
        super.onResume();
        Log.i(TAG, "ThirdActivity onResume");
    }
    
    @Override
    protected void onPause()
    {
        super.onPause();
        Log.i(TAG, "ThirdActivity onPause");
    }
    
    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        super.onSaveInstanceState(outState);
        Log.i(TAG, "ThirdActivity onSaveInstanceState");
        outState.putString("name", "xiaoluo");
    }
    
    @Override
    protected void 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
    protected void onStop()
    {
        super.onStop();
        Log.i(TAG, "ThirdActivity onStop");
    }
    
    @Override
    protected void onRestart()
    {
        super.onRestart();
        Log.i(TAG, "ThirdActivity onRestart");
    }
    
    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        Log.i(TAG, "ThirdActivity onDestroy");
    }
}

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

    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        super.onSaveInstanceState(outState);
        Log.i(TAG, "ThirdActivity onSaveInstanceState");
        outState.putString("name", "xiaoluo");
    }    

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

protected void 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
            public void onClick(View view)
            {
                Intent intent = new Intent();
                intent.setClass(ThirdActivity.this, FourthActivity.class);
                startActivity(intent);
            }
        });
        
    }
@Override
    protected void 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的狀態。

咱們來看看實驗結果:

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

4、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控件的狀態:

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

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

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

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

相關文章
相關標籤/搜索