我一直在安卓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分钟,没有发现任何明显的东西。


当前回答

这是Android开发的经典“陷阱”。这里有两个问题:

有一个微妙的Android框架错误,它在开发过程中使应用程序堆栈管理变得非常复杂,至少在旧版本上如此(不完全确定是否/何时/如何修复)。我将在下面讨论这个bug。管理此问题的“正常”或预期方式本身就相当复杂,因为onPause/onResume和onSaveInstanceState/onRestoreInstanceState具有双重性

浏览所有这些线程,我怀疑很多时候开发人员都在同时讨论这两个不同的问题。。。因此,所有的困惑和“这对我不起作用”的报道。

首先,要澄清“预期”行为:onSaveInstance和onRestoreInstance是脆弱的,仅适用于暂时状态。预期用途(据我所知)是在手机旋转(方向改变)时处理“活动”娱乐。换言之,当您的“活动”在逻辑上仍处于“顶部”,但仍必须由系统重新实例化时,即为预期用途。保存的Bundle不会在进程/内存/GC之外持久化,因此如果您的活动进入后台,您就不能真正依赖它。是的,也许你的“活动”的内存可以保存到后台并逃脱GC,但这并不可靠(也不可预测)。

因此,如果您有一个场景,其中在应用程序的“启动”之间应该保持有意义的“用户进度”或状态,则指南是使用onPause和onResume。你必须自己选择并准备一个持久性商店。

但是,有一个非常令人困惑的错误,它使所有这一切变得复杂。详情如下:

当从Eclipse启动应用程序时,活动堆栈在第一次运行期间的行为不正确(#369007463)市场/浏览器应用程序安装程序允许第二个实例脱离应用程序(#36911210)

基本上,如果您的应用程序使用SingleTask标志启动,然后稍后从主屏幕或启动程序菜单启动它,那么随后的调用将创建一个新任务。。。你的应用程序将有两个不同的实例驻留在同一堆栈中。。。这变得非常奇怪,非常快。这似乎发生在开发期间启动应用程序时(即从Eclipse或IntelliJ),因此开发人员经常遇到这种情况。但也通过一些应用商店更新机制(因此它也会影响用户)。

我在这些线程中挣扎了几个小时,才意识到我的主要问题是这个bug,而不是预期的框架行为。一个很好的总结和解决方法(更新:见下文)似乎来自用户@kaciula的回答:

主页键按下行为

2013年6月更新:几个月后,我终于找到了“正确”的解决方案。您不需要自己管理任何有状态的startedApp标志。您可以从框架中检测到这一点并适当地保释。我在启动程序活动开始时使用此选项。创建:

if (!isTaskRoot()) {
    Intent intent = getIntent();
    String action = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
        finish();
        return;
    }
}

其他回答

onSaveInstanceState()用于瞬时数据(在onCreate()/onRestoreInstanceState(()中还原),onPause()用于持久数据(在on Resume()中恢复)。从Android技术资源:

如果活动正在停止,Android会调用onSaveInstanceState(),并且可能会在恢复之前被终止!这意味着它应该存储在重新启动“活动”时重新初始化为相同条件所需的任何状态。它与onCreate()方法相对应,实际上传递给onCreate(()的savedInstanceState Bundle与您在onSaveInstanceState()方法中构造的outState Bundle相同。onPause()和onResume()也是互补的方法。onPause()总是在“活动”结束时调用,即使我们启动了它(例如,使用finish()调用)。我们将使用它将当前注释保存回数据库。好的做法是释放所有可以在onPause()期间释放的资源,以便在处于被动状态时占用更少的资源。

科特林

必须重写onSaveInstanceState和onRestoreInstanceState以存储和检索要持久化的变量

生命周期图

存储变量

public override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)

    // prepare variables here
    savedInstanceState.putInt("kInt", 10)
    savedInstanceState.putBoolean("kBool", true)
    savedInstanceState.putDouble("kDouble", 4.5)
    savedInstanceState.putString("kString", "Hello Kotlin")
}

检索变量

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

    val myInt = savedInstanceState.getInt("kInt")
    val myBoolean = savedInstanceState.getBoolean("kBool")
    val myDouble = savedInstanceState.getDouble("kDouble")
    val myString = savedInstanceState.getString("kString")
    // use variables here
}

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

解决这个问题的简单快捷方法是使用IcePick

首先,在app/build.gradle中设置库

repositories {
  maven {url "https://clojars.org/repo/"}
}
dependencies {
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
}

现在,让我们看看下面的示例,如何在“活动”中保存状态

public class ExampleActivity extends Activity {
  @State String username; // This will be automatically saved and restored

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

它适用于Activities、Fragments或任何需要在Bundle上序列化其状态的对象(例如,迫击炮的ViewPresenter)

Icepick还可以为自定义视图生成实例状态代码:

class CustomView extends View {
  @State int selectedPosition; // This will be automatically saved and restored

  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());
  }

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
  }

  // You can put the calls to Icepick into a BaseCustomView and inherit from it
  // All Views extending this CustomView automatically have state saved/restored
}

当“活动”转到后台时,确实会调用onSaveInstanceState()。

引用文档:“在活动被终止之前调用此方法,以便在将来某个时候返回时恢复其状态。”来源