【问题标题】:Android View - What is automatically saved and restored in an ActivityAndroid 视图 - 在 Activity 中自动保存和恢复的内容
【发布时间】:2018-01-01 00:39:04
【问题描述】:

我是 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 小部件中的文本值)。

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

【问题讨论】:

  • "默认情况下会自动保存一些activity状态信息。你的布局中视图的状态有一个唯一的ID(由布局中的android:id属性定义)是重新创建活动时保存和恢复。 在这种情况下,用户在 EditText 视图中输入的值通常会在重新创建活动时保留。" ref: @987654322 @

标签: android activity-lifecycle android-savedstate


【解决方案1】:

解释保存状态的 Android 文档和一篇关于在活动和片段中保存状态的非常好的文章。

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

当您的 Activity 因用户按下“返回”或 Activity 自行结束而被销毁时,系统对 Activity 实例的概念将永远消失,因为该行为表明不再需要该 Activity。但是,如果系统由于系统限制(而不是正常的应用程序行为)而破坏了活动,那么虽然实际的 Activity 实例已经消失,但系统会记住它的存在,因此如果用户导航回它,系统会创建使用一组保存的数据来描述活动被销毁时的状态的活动的新实例。系统用来恢复之前状态的保存数据称为实例状态,是存储在Bundle对象中的键值对的集合。

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

保存您的活动状态 当您的活动开始停止时,系统会调用onSaveInstanceState() 方法,以便您的活动可以使用键值对集合保存状态信息。此方法的默认实现保存有关 Activity 视图层次结构状态的临时信息,例如 EditText 小部件中的文本或 ListView 小部件的滚动位置。

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

要为您的 Activity 保存其他状态信息,您必须覆盖 onSaveInstanceState() 并将键值对添加到在您的 Activity 意外销毁时保存的 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);
}

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

要保存持久性数据,例如用户偏好或数据库数据,您应该在活动处于前台时抓住适当的机会。如果没有这样的机会出现,您应该在onStop() 方法期间保存这些数据。

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

因为onCreate()方法被调用,无论系统是创建一个新的Activity实例还是重新创建前一个实例,你必须在尝试读取它之前检查状态Bundle是否为空。如果为 null,则系统正在创建 Activity 的新实例,而不是恢复之前被破坏的实例。

例如,下面的代码sn-p显示了如何恢复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
    }
    ...
}

您可以选择实现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);
}

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

【讨论】:

  • 查看我的第二个答案,了解更多详细信息和代码,以解释哪些数据是免费保存的,哪些数据需要您编码!如果它解释了,那么请接受答案并投票!
  • 我的错,你回答我的问题。感谢 JRG。
【解决方案2】:

这里有一个例子来解释你......

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

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

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

这是截图中的事件顺序。

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

步骤顺序

代码

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>

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");
    }
}

activity_main.xml

<?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>

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 2013-11-08
  • 2016-02-29
  • 2017-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多