Android视图 - 活动中自动保存和恢复的内容

Orw*_*wel 7 android activity-lifecycle android-savedstate

我是Android的初学者.

在Android中,一些通用元素可以在onSaveInstanceState/中自动保存/恢复onRestoreInstanceState.

例如,EditText保存/恢复Text属性,RatingBar保存/恢复Rating属性...

我从一些测试中看到,但我在文档中找不到任何相关内容.

如果没有我的干预,我怎么能知道隐含地保存/恢复了什么?

例如,我可以找到EditText.Text自动保存/恢复的地方?

我特别不想测试所有属性.


从JRG回复编辑:

https://developer.android.com/guide/components/activities/activity-lifecycle.html

保存活动状态当您的活动开始停止时,系统将调用onSaveInstanceState()方法<...>此方法的默认实现保存有关活动视图层次结构状态的瞬态信息,例如EditText小部件中的文本或ListView小部件的滚动位置.

我怎么知道保存/恢复的默认实现是什么?

重读JRG后的第二次编辑回答:

默认情况下,系统使用Bundle实例状态来保存有关活动布局中每个View对象的信息(例如输入到EditText小部件中的文本值).

默认实现保存/恢复元素视图的所有状态.

JRG*_*JRG 7

Android文档,其中介绍了有关保存状态的知识,以及一篇有关在活动和片段中保存状态的很好的文章。

保存和恢复活动状态在某些情况下,由于正常的应用程序行为,您的活动被销毁,例如,当用户按下“后退”按钮或您的活动通过调用finish()方法发出自己的销毁信号时。如果该活动处于“已停止”状态并且很长时间没有使用,或者前台活动需要更多资源,则系统还可能破坏包含您的活动的进程以恢复内存。

当您的活动由于用户按下Back或活动本身而被破坏时,该Activity实例的系统概念就永远消失了,因为行为表明不再需要该活动。但是,如果系统由于系统限制(而不是正常的应用行为)破坏了活动,则尽管实际的Activity实例已消失,但系统会记住该实例的存在,因此,如果用户导航回该实例,则系统会创建一个新实例。活动的实例,使用一组保存的数据来描述活动被销毁时的状态。系统用于恢复先前状态的已保存数据称为实例状态,是存储在Bundle对象中的键/值对的集合。

默认情况下,系统使用Bundle实例状态在您的活动布局中保存有关每个View对象的信息(例如,输入到EditText小部件中的文本值)。因此,如果您的活动实例被销毁并重新创建,则布局状态将恢复为之前的状态,而无需您执行任何代码。但是,您的活动可能具有更多要还原的状态信息,例如跟踪用户在活动中进度的成员变量。

保存活动状态 当您的活动开始停止时,系统将调用onSaveInstanceState()方法,以便您的活动可以使用一组键值对保存状态信息。此方法的默认实现保存有关活动的视图层次结构状态的瞬时信息,例如EditText小部件中的文本或ListView小部件的滚动位置。您的应用应在onPause()方法之后和onStop()之前实现onSaveInstanceState()回调。不要在onPause()中实现此回调。

警告:您必须始终调用onSaveInstanceState()的超类实现,以便默认实现可以保存视图层次结构的状态。

要保存活动的其他状态信息,必须重写onSaveInstanceState()并将键值对添加到在意外破坏活动的情况下保存的Bundle对象中。例如:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...


@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);


    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}
Run Code Online (Sandbox Code Playgroud)

注意:为了使Android系统恢复您活动中视图的状态,每个视图必须具有由android:id属性提供的唯一ID。

为了保存持久性数据,例如用户首选项或数据库数据,当活动处于前台时,应该抓住适当的机会。如果没有这种机会出现,则应在onStop()方法期间保存此类数据。

恢复活动状态 在先前销毁了活动之后重新创建活动时,您可以从捆绑包中恢复保存的状态,系统会将其传递给活动。onCreate()和onRestoreInstanceState()回调方法都接收包含实例状态信息的同一个Bundle。

由于onCreate()方法是系统是创建活动的新实例还是重新创建活动的实例而调用的,因此在尝试读取状态Bundle之前,必须检查其状态是否为null。如果为空,则系统将创建活动的新实例,而不是还原先前被破坏的活动。

例如,以下代码片段显示了如何在onCreate()中还原某些状态数据:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first


    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

您可以选择实现onRestoreInstanceState(),而不是在onCreate()期间恢复状态,系统在onStart()方法之后调用该方法。仅当存在要还原的保存状态时,系统才调用onRestoreInstanceState(),因此您无需检查Bundle是否为空:

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);


    // Restore state members from saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
Run Code Online (Sandbox Code Playgroud)

注意:始终调用onRestoreInstanceState()的超类实现,以便默认实现可以恢复视图层次结构的状态。


JRG*_*JRG 5

这是一个例子来向您解释...

  • 默认保存什么以及如何保存?
  • 哪些数据需要您添加代码才能保存?

我创建了一个简单的 Android 项目,共有 4 个数据点,这些数据点在应用程序生命周期的某个时间点具有一定的价值。

  1. 活动内部变量saveMe
  2. 活动内部变量saveMeNot
  3. 视图的 EditText withid(有 android:id)
  4. View 的 EditText(没有 android:id)

以下是屏幕截图中的事件顺序。

  1. 启动安卓应用程序
  2. 单击SAVE按钮设置内部变量saveMe和的值saveMeNot。将显示它Toast保存了两个变量的值。
  3. 在编辑文本中键入一些文本,例如“Hello”和“Hi”。这将在两个编辑文本中设置文本。
  4. 旋转屏幕,即改变方向。接下来会发生......
    • Android 将保存android:idactivity_main.xml. 这里只有 Hello 会被保存,因为输入 Hello 的 EditText 具有android:id=@+id/withId. Android 不会自动保存另一个包含文本 Hi 的 EditText,因为它没有任何android:id. 这是您免费获得的(前提是您已经android:id定义了所有观点)。如果您有扩展视图的自定义视图,那么它们也已android:id定义。
    • saveMeAndroid 还调用 onSaveInstanceState 和 onRestoreInstanceState,这使您能够存储 Activity 的所有内部变量(即和 )的状态saveMeNot。您必须为其编写代码,否则状态就会丢失。就像在我的例子中一样,我保存了 of 的状态saveMe,而不是 for 的状态saveMeNot。这是您无法免费获得的东西,即您必须为其编写代码。
  5. 单击按钮查看和的CLICK ME值,您将看到仅显示您保存和检索的值saveMesaveMeNotsaveMeonSaveInstanceStateonRestoreInstanceState

步骤顺序

在此输入图像描述

代码

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="test.saveinstance">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Run Code Online (Sandbox Code Playgroud)

MainActivity.java

package test.saveinstance;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    // will save in bundle in onSaveInstanceState
    private int saveMe;

    // will not save in bundle in onSaveInstanceState
    private int saveMeNot;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // do some action that generates values for
        // activity specific variables i.e. saveMe
        // and saveMeNot
        Button saveButton = (Button) findViewById(R.id.save);
        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                saveMe = 10;
                saveMeNot = 20;
                Toast.makeText(getApplicationContext(), "SAVED: saveMe: " + saveMe + ";saveMeNot: " + saveMeNot, Toast.LENGTH_LONG).show();
            }
        });

        // will be used to display value of
        // saveMe and saveMeNot after orientation
        // changes.
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getApplicationContext(), "DISPLAY: saveMe: " + saveMe + ";saveMeNot: " + saveMeNot, Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // save saveMe in bundle
        outState.putInt("saveMe", saveMe);
        super.onSaveInstanceState(outState);
        Log.d("TEST", "Saving saveMe in bundle during orientation change");
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        // retrieve saveMe from bundle
        saveMe = savedInstanceState.getInt("saveMe");
        Log.d("TEST", "Retrieving saveMe in bundle during orientation change");
    }
}
Run Code Online (Sandbox Code Playgroud)

活动主文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="test.saveinstance.MainActivity">

    <EditText
        android:id="@+id/withId"
        android:layout_marginTop="30dp"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:gravity="center"
        android:hint="Type Here (has android:id)" />

    <EditText
        android:layout_below="@id/withId"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:gravity="center"
        android:hint="Type Here (doesn't have android:id)" />

    <Button
        android:id="@+id/button"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="10dp"
        android:text="Click Me"
        android:layout_width="match_parent"
        android:layout_height="50dp" />

    <Button
        android:id="@+id/save"
        android:layout_above="@id/button"
        android:layout_marginBottom="10dp"
        android:text="Save"
        android:layout_width="match_parent"
        android:layout_height="50dp" />

</RelativeLayout>
Run Code Online (Sandbox Code Playgroud)