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() 方法中来作

相关文章
相关标签/搜索