我一直在安卓SDK平台上工作,现在还不清楚如何保存应用程序的状态。因此,考虑到“你好,Android”示例的这个小的重新设计:

package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

我认为这对于最简单的情况来说已经足够了,但无论我如何离开应用程序,它总是以第一条消息来响应。

我确信解决方案就像重写onPause之类的东西一样简单,但我已经在文档中翻了大约30分钟,没有发现任何明显的东西。


当前回答

直接回答原始问题。savedInstancestate为空,因为从未重新创建“活动”。

只有在以下情况下,才能使用状态捆绑包重新创建“活动”:

配置更改,例如更改方向或电话语言,这可能需要创建新的活动实例。操作系统破坏活动后,您从后台返回应用程序。

Android会在内存压力下或在后台工作一段时间后破坏后台活动。

测试hello world示例时,有几种方法可以离开并返回“活动”。

按下后退按钮后,“活动”结束。重新启动应用程序是一个全新的例子。你根本没有从后台恢复。当您按下主页按钮或使用任务切换器时,“活动”将进入后台。当导航回应用程序时,只有在必须销毁“活动”时才会调用onCreate。

在大多数情况下,如果你只是按下home键,然后再次启动应用程序,则不需要重新创建活动。它已经存在于内存中,因此不会调用onCreate()。

在“设置”->“开发人员选项”下有一个名为“不保留活动”的选项。当它被启用时,安卓系统总是会破坏活动,并在后台重新创建它们。这是一个在开发时保持启用状态的好选项,因为它模拟了最坏的情况。(内存不足的设备会一直回收您的活动)。

其他的答案很有价值,因为它们教会了你正确的存储状态的方法,但我觉得它们并没有真正回答为什么你的代码没有按照你期望的方式工作。

其他回答

2020年,我们有一些变化:

如果您希望“活动”在进程终止并再次启动后恢复其状态,则可能需要使用“保存状态”功能。以前,需要重写Activity中的两个方法:onSaveInstanceState和onRestoreInstanceState。您还可以在onCreate方法中访问恢复的状态。类似地,在Fragment中,您有onSaveInstanceState方法可用(恢复的状态在onCreate、onCreateView和onActivityCreated方法中可用)。

从AndroidX SavedState 1.0.0开始,它是AndroidX Activity和AndroidX Fragment的依赖项,您可以访问SavedStateRegistry。您可以从Activity/Fragment获取SavedStateRegistry,然后注册SavedStateProvider:

class MyActivity : AppCompatActivity() {

  companion object {
    private const val MY_SAVED_STATE_KEY = "MY_SAVED_STATE_KEY "
    private const val SOME_VALUE_KEY = "SOME_VALUE_KEY "
  }
    
  private lateinit var someValue: String
  private val savedStateProvider = SavedStateRegistry.SavedStateProvider {    
    Bundle().apply {
      putString(SOME_VALUE_KEY, someValue)
    }
  }
  
  override fun onCreate(savedInstanceState: Bundle?) {    
    super.onCreate(savedInstanceState)
    savedStateRegistry.registerSavedStateProvider(MY_SAVED_STATE_KEY, savedStateProvider)
    someValue = savedStateRegistry.consumeRestoredStateForKey(MY_SAVED_STATE_KEY)?.getString(SOME_VALUE_KEY) ?: ""
  }
  
}

如您所见,SavedStateRegistry强制您对数据使用键。这可以防止您的数据被附加到同一Activity/Fragment的另一个SavedStateProvider损坏。此外,您还可以将SavedStateProvider提取到另一个类中,以便通过使用所需的抽象来处理数据,从而在应用程序中实现干净的保存状态行为。

我想我找到了答案。让我简单地说一下我做了什么:

假设我有两个活动,即活动1和活动2,我正在从活动1导航到活动2(我在活动2中做了一些工作),然后通过单击活动1中的按钮再次返回活动1。现在在这个阶段,我想回到活动2,我想看到我的活动2处于上次离开活动2时的相同状态。

对于上述场景,我所做的是在清单中进行了如下更改:

<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>

在活动1的按钮点击事件中,我这样做了:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);

在活动2的按钮点击事件中,我这样做了:

Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);

现在将要发生的是,我们在activity2中所做的任何更改都不会丢失,并且我们可以以与之前相同的状态查看activity2。

我相信这就是答案,这对我来说很好。如果我错了,请纠正我。

哪些要保存,哪些不保存?

有没有想过,当方向改变时,为什么EditText中的文本会自动保存?这个答案是给你的。

当“活动”的实例被破坏并且系统重新创建新实例时(例如,配置更改)。它尝试使用一组保存的旧活动状态(实例状态)数据重新创建它。

实例状态是存储在Bundle对象中的键值对的集合。

例如,默认情况下,系统将视图对象保存在捆绑包中。

EditText中的文本在ListView中滚动位置等。

如果需要将另一个变量保存为实例状态的一部分,则应使用OVERRIDE onSavedInstanceState(Bundle savedinstaneState)方法。

例如,GameActivity中的int currentScore

有关保存数据时onSavedInstanceState(Bundle savedinstaneState)的详细信息

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

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

所以如果你忘了打电话super.onSaveInstanceState(savedInstanceState);默认行为无法工作,即EditText中的文本无法保存。

要选择哪一项来恢复“活动”状态?

 onCreate(Bundle savedInstanceState)

OR

onRestoreInstanceState(Bundle savedInstanceState)

这两种方法都获得相同的Bundle对象,因此在哪里编写恢复逻辑并不重要。唯一的区别是,在onCreate(Bundle savedInstanceState)方法中,您必须给出一个空检查,而在后一种情况下不需要它。其他答案已经有了代码片段。你可以参考他们。

有关onRestoreInstanceState(Bundle savedinstaneState)的详细信息

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

    // Restore state members from the saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
}

始终调用super.onRestoreInstanceState(savedInstanceState);以便系统默认还原视图层次结构

奖金

只有当用户打算返回“活动”时,系统才会调用onSaveInstanceState(Bundle savedInstanceState)。例如,您正在使用App X,突然接到一个电话。移动到调用方应用程序并返回到应用程序X。在这种情况下,将调用onSaveInstanceState(Bundle savedInstanceState)方法。

但如果用户按下后退按钮,请考虑这一点。假设用户不打算返回“活动”,因此在这种情况下,系统不会调用onSaveInstanceState(Bundle savedInstanceState)。重要的是,在保存数据时,您应该考虑所有情况。

相关链接:

演示默认行为Android官方文档。

我的同事写了一篇文章,解释了Android设备上的应用程序状态,包括活动生命周期和状态信息的解释,如何存储状态信息,以及保存到状态Bundle和SharedPreferences。看看这里。

本文涵盖三种方法:

使用实例状态包存储应用程序生存期(即临时)的本地变量/UI控制数据

[Code sample – Store state in state bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);

  super.onSaveInstanceState(savedInstanceState);
}

使用共享首选项在应用程序实例之间(即永久)存储本地变量/UI控制数据

[Code sample – store state in SharedPreferences]
@Override
protected void onPause()
{
  super.onPause();

  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  editor.putString(“Name”, strName); // value to store
  editor.putString(“Email”, strEmail); // value to store
  editor.putBoolean(“TandC”, blnTandC); // value to store
  // Commit to storage
  editor.commit();
}

使用保留的非配置实例在应用程序生存期内的活动之间保持对象实例在内存中的活动状态

[Code sample – store object instance]
private cMyClassType moInstanceOfAClass; // Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance()
{
  if (moInstanceOfAClass != null) // Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}

Kotlin解决方案:对于onSaveInstanceState中的自定义类保存,您可以将类转换为JSON字符串并使用Gson转换进行恢复,对于单字符串、双精度、Int、Long值保存和恢复,如下所示。以下示例适用于片段和活动:

对于活动:

对于将数据放入saveInstanceState:

override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)

        //for custom class-----
        val gson = Gson()
        val json = gson.toJson(your_custom_class)
        outState.putString("CUSTOM_CLASS", json)

        //for single value------
        outState.putString("MyString", stringValue)
        outState.putBoolean("MyBoolean", true)
        outState.putDouble("myDouble", doubleValue)
        outState.putInt("MyInt", intValue)
    }

还原数据:

 override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    //for custom class restore
    val json = savedInstanceState?.getString("CUSTOM_CLASS")
    if (!json!!.isEmpty()) {
        val gson = Gson()
        testBundle = gson.fromJson(json, Session::class.java)
    }

  //for single value restore

   val myBoolean: Boolean = savedInstanceState?.getBoolean("MyBoolean")
   val myDouble: Double = savedInstanceState?.getDouble("myDouble")
   val myInt: Int = savedInstanceState?.getInt("MyInt")
   val myString: String = savedInstanceState?.getString("MyString")
 }

您也可以在Activity onCreate上恢复它。

对于碎片:

对于saveInstanceState中的put类:

 override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val gson = Gson()
        val json = gson.toJson(customClass)
        outState.putString("CUSTOM_CLASS", json)
    }

还原数据:

 override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        //for custom class restore
        if (savedInstanceState != null) {
            val json = savedInstanceState.getString("CUSTOM_CLASS")
            if (!json!!.isEmpty()) {
                val gson = Gson()
                val customClass: CustomClass = gson.fromJson(json, CustomClass::class.java)
            }
        }

      // for single value restore
      val myBoolean: Boolean = savedInstanceState.getBoolean("MyBoolean")
      val myDouble: Double = savedInstanceState.getDouble("myDouble")
      val myInt: Int = savedInstanceState.getInt("MyInt")
      val myString: String = savedInstanceState.getString("MyString")
    }