Android 查看 - Activity 中自动保存和恢复的内容
Android View - What is automatically saved and restored in an Activity
我是 Android 的初学者。
在Android中,一些泛型元素可以在onSaveInstanceState
/onRestoreInstanceState
中自动saved/restored。
比如一个EditText
saves/restores Text
属性,一个RatingBar
saves/restores Rating
属性...
我从一些测试中看到,但我在文档中找不到任何相关信息。
我怎么能在没有我干预的情况下隐含地知道什么是 saved/restored?
例如,我在哪里可以找到 EditText.Text
自动 saved/restored?
我特别不想测试所有属性。
编辑自 JRG 答案:
https://developer.android.com/guide/components/activities/activity-lifecycle.html
Save your activity state As your activity begins to stop, the system
calls the onSaveInstanceState() method<...> The default implementation
of this method saves transient information about the state of the
activity's view hierarchy, such as the text in an EditText widget or
the scroll position of a ListView widget.
我怎么知道 save/restore 的默认实现是什么?
重读 JRG 答案后的第二次编辑:
By default, the system uses the Bundle instance state to save information about >each View object in your activity layout (such as the text value entered into >an EditText widget).
默认实现saves/restores元素视图的所有状态。
Android 解释保存状态的文档和一篇关于在 activity 和片段中保存状态的非常好的文章。
Android Documentation:
https://developer.android.com/guide/components/activities/activity-lifecycle.html
Additional Article:
https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
Saving and restoring activity state There are a few scenarios in which your activity is destroyed due to normal app behavior, such as when the user presses the Back button or your activity signals its own destruction by calling the finish()
method. The system may also destroy the process containing your activity to recover memory if the activity is in the Stopped state and hasn't been used in a long time, or if the foreground activity requires more resources.
When your activity is destroyed because the user presses "Back" or the activity finishes itself, the system's concept of that Activity
instance is gone forever because the behavior indicates the activity is no longer needed. However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actual Activity
instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the instance state and is a collection of key-value pairs stored in a Bundle
object.
By default, the system uses the Bundle
instance state to save information about each View
object in your activity layout (such as the text value entered into an EditText widget). So, if your activity instance is destroyed and recreated, the state of the layout is restored to its previous state with no code required by you. However, your activity might have more state information that you'd like to restore, such as member variables that track the user's progress in the activity.
Save your activity state
As your activity begins to stop, the system calls the onSaveInstanceState()
method so your activity can save state information with a collection of key-value pairs. The default implementation of this method saves transient information about the state of the activity's view hierarchy, such as the text in an EditText
widget or the scroll position of a ListView
widget.
Caution: You must always call the superclass implementation of onSaveInstanceState()
so the default implementation can save the state of the view hierarchy.
To save additional state information for your activity, you must override onSaveInstanceState()
and add key-value pairs to the Bundle
object that is saved in the event that your activity is destroyed unexpectedly. For example:
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);
}
Note: In order for the Android system to restore the state of the views in your activity, each view must have a unique ID, supplied by the android:id
attribute.
To save persistent data, such as user preferences or data for a database, you should take appropriate opportunities when your activity is in the foreground. If no such opportunity arises, you should save such data during the onStop()
method.
Restore your activity state
When your activity is recreated after it was previously destroyed, you can recover your saved state from the Bundle that the system passes to your activity. Both the onCreate()
and onRestoreInstanceState()
callback methods receive the same Bundle that contains the instance state information.
Because the onCreate()
method is called whether the system is creating a new instance of your activity or recreating a previous one, you must check whether the state Bundle
is null before you attempt to read it. If it is null, then the system is creating a new instance of the activity, instead of restoring a previous one that was destroyed.
For example, the following code snippet shows how you can restore some state data in 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
}
...
}
Instead of restoring the state during onCreate()
you may choose to implement onRestoreInstanceState()
, which the system calls after the onStart()
method. The system calls onRestoreInstanceState()
only if there is a saved state to restore, so you do not need to check whether the Bundle is null:
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);
}
Caution: Always call the superclass implementation of onRestoreInstanceState()
so the default implementation can restore the state of the view hierarchy.
这里举个例子给大家解释一下...
- 默认保存什么以及如何保存?
- 哪些数据需要您添加代码才能保存?
我创建了一个简单的 android 项目,该项目共有 4 个数据点,这些数据点在应用程序生命周期的某个时间点具有一定的价值。
- Activity内部变量
saveMe
- Activity内部变量
saveMeNot
- View 的 EditText
withid
(有 android:id)
- 视图的 EditText(没有 android:id)
以下是屏幕截图中的事件顺序。
- 启动 Android 应用程序
- 单击
SAVE
按钮设置内部变量 saveMe
和 saveMeNot
的值。将显示 Toast
它保存了两个变量的值。
- 在编辑文本中键入一些文本,例如 Hello 和 Hi。这将在两个编辑文本中设置文本。
- 旋转屏幕即方向改变。以下将发生......
- Android 将保存在
activity_main.xml
中定义了 android:id
的所有视图的值。这里只有 Hello 会被保存,因为输入 Hello 的 EditText 有一个 android:id=@+id/withId
。 Android 不会自动保存另一个包含文本 Hi 的 EditText,因为它没有任何 android:id
。这是您免费获得的(前提是您的所有视图都定义了 android:id
)。如果您有扩展视图的自定义视图,那么它们也有 android:id
定义。
- Android 还调用 onSaveInstanceState 和 onRestoreInstanceState,这使您能够存储 activity 的所有内部变量的状态,即
saveMe
和 saveMeNot
。您必须为其编码,否则状态将丢失。就像在我的示例中一样,我保存了 saveMe
的状态而不是 saveMeNot
的状态。这是您无法免费获得的东西,即您必须为其编写代码。
- 单击
CLICK ME
按钮查看 saveMe
和 saveMeNot
的值,您将看到仅显示 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>
我是 Android 的初学者。
在Android中,一些泛型元素可以在onSaveInstanceState
/onRestoreInstanceState
中自动saved/restored。
比如一个EditText
saves/restores Text
属性,一个RatingBar
saves/restores Rating
属性...
我从一些测试中看到,但我在文档中找不到任何相关信息。
我怎么能在没有我干预的情况下隐含地知道什么是 saved/restored?
例如,我在哪里可以找到 EditText.Text
自动 saved/restored?
我特别不想测试所有属性。
编辑自 JRG 答案:
https://developer.android.com/guide/components/activities/activity-lifecycle.html
Save your activity state As your activity begins to stop, the system calls the onSaveInstanceState() method<...> The default implementation of this method saves transient information about the state of the activity's view hierarchy, such as the text in an EditText widget or the scroll position of a ListView widget.
我怎么知道 save/restore 的默认实现是什么?
重读 JRG 答案后的第二次编辑:
By default, the system uses the Bundle instance state to save information about >each View object in your activity layout (such as the text value entered into >an EditText widget).
默认实现saves/restores元素视图的所有状态。
Android 解释保存状态的文档和一篇关于在 activity 和片段中保存状态的非常好的文章。
Android Documentation:
https://developer.android.com/guide/components/activities/activity-lifecycle.htmlAdditional Article:
https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
Saving and restoring activity state There are a few scenarios in which your activity is destroyed due to normal app behavior, such as when the user presses the Back button or your activity signals its own destruction by calling the
finish()
method. The system may also destroy the process containing your activity to recover memory if the activity is in the Stopped state and hasn't been used in a long time, or if the foreground activity requires more resources.
When your activity is destroyed because the user presses "Back" or the activity finishes itself, the system's concept of that
Activity
instance is gone forever because the behavior indicates the activity is no longer needed. However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actualActivity
instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the instance state and is a collection of key-value pairs stored in aBundle
object.
By default, the system uses the
Bundle
instance state to save information about eachView
object in your activity layout (such as the text value entered into an EditText widget). So, if your activity instance is destroyed and recreated, the state of the layout is restored to its previous state with no code required by you. However, your activity might have more state information that you'd like to restore, such as member variables that track the user's progress in the activity.
Save your activity state As your activity begins to stop, the system calls the
onSaveInstanceState()
method so your activity can save state information with a collection of key-value pairs. The default implementation of this method saves transient information about the state of the activity's view hierarchy, such as the text in anEditText
widget or the scroll position of aListView
widget.
Caution: You must always call the superclass implementation of
onSaveInstanceState()
so the default implementation can save the state of the view hierarchy.
To save additional state information for your activity, you must override
onSaveInstanceState()
and add key-value pairs to theBundle
object that is saved in the event that your activity is destroyed unexpectedly. For example:
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);
}
Note: In order for the Android system to restore the state of the views in your activity, each view must have a unique ID, supplied by the
android:id
attribute.
To save persistent data, such as user preferences or data for a database, you should take appropriate opportunities when your activity is in the foreground. If no such opportunity arises, you should save such data during the
onStop()
method.
Restore your activity state When your activity is recreated after it was previously destroyed, you can recover your saved state from the Bundle that the system passes to your activity. Both the
onCreate()
andonRestoreInstanceState()
callback methods receive the same Bundle that contains the instance state information.
Because the
onCreate()
method is called whether the system is creating a new instance of your activity or recreating a previous one, you must check whether the stateBundle
is null before you attempt to read it. If it is null, then the system is creating a new instance of the activity, instead of restoring a previous one that was destroyed.
For example, the following code snippet shows how you can restore some state data in
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
}
...
}
Instead of restoring the state during
onCreate()
you may choose to implementonRestoreInstanceState()
, which the system calls after theonStart()
method. The system callsonRestoreInstanceState()
only if there is a saved state to restore, so you do not need to check whether the Bundle is null:
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);
}
Caution: Always call the superclass implementation of
onRestoreInstanceState()
so the default implementation can restore the state of the view hierarchy.
这里举个例子给大家解释一下...
- 默认保存什么以及如何保存?
- 哪些数据需要您添加代码才能保存?
我创建了一个简单的 android 项目,该项目共有 4 个数据点,这些数据点在应用程序生命周期的某个时间点具有一定的价值。
- Activity内部变量
saveMe
- Activity内部变量
saveMeNot
- View 的 EditText
withid
(有 android:id) - 视图的 EditText(没有 android:id)
以下是屏幕截图中的事件顺序。
- 启动 Android 应用程序
- 单击
SAVE
按钮设置内部变量saveMe
和saveMeNot
的值。将显示Toast
它保存了两个变量的值。 - 在编辑文本中键入一些文本,例如 Hello 和 Hi。这将在两个编辑文本中设置文本。
- 旋转屏幕即方向改变。以下将发生......
- Android 将保存在
activity_main.xml
中定义了android:id
的所有视图的值。这里只有 Hello 会被保存,因为输入 Hello 的 EditText 有一个android:id=@+id/withId
。 Android 不会自动保存另一个包含文本 Hi 的 EditText,因为它没有任何android:id
。这是您免费获得的(前提是您的所有视图都定义了android:id
)。如果您有扩展视图的自定义视图,那么它们也有android:id
定义。 - Android 还调用 onSaveInstanceState 和 onRestoreInstanceState,这使您能够存储 activity 的所有内部变量的状态,即
saveMe
和saveMeNot
。您必须为其编码,否则状态将丢失。就像在我的示例中一样,我保存了saveMe
的状态而不是saveMeNot
的状态。这是您无法免费获得的东西,即您必须为其编写代码。
- Android 将保存在
- 单击
CLICK ME
按钮查看saveMe
和saveMeNot
的值,您将看到仅显示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>