【问题标题】:Why does saving a Hashtable of non-Parcelable objects in onSaveInstanceState() sometimes work?为什么在 onSaveInstanceState() 中保存非 Parcelable 对象的哈希表有时会起作用?
【发布时间】:2011-03-03 21:02:24
【问题描述】:

在阅读了一本介绍性的 Android 编程书籍之后,我想修改示例应用程序,以巩固我对一些没有真正涉及的主题的理解。在进行更改时,我犯了一个错误,但我很好奇为什么该错误在某些情况下有效,而在其他情况下无效。

应用程序中的一个活动将一系列问题存储在 Hashtable<Integer, Question> 中,其中 Question 是一个包含一个 int 和两个字符串的小类。正如最初写的那样,活动从每个onCreate() 的服务器上下载问题,所以我想实现onSaveInstanceState() 以防止一些冗余下载。 onSaveInstanceState() 使用 putSerializable() 将 Hashtable 保存到 Bundle 中。

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
            // mQuestions is a member variable of 
            // type Hashtable<Integer, Question>
    if (mQuestions != null && mQuestions.size() > 0) {
        outState.putSerializable(SAVED_QUESTIONS, mQuestions);
    }
}

甚至在我知道 Parcelable 是什么或如何实现它之前,它就非常适合更改屏幕方向。我只知道当我按下模拟器的主页键和应用程序无声无息地崩溃时出现问题,没有 LogCat 输出。堆栈跟踪使我查找 Parcelable 并让 Question 实现它。

我的问题不是我做错了什么。问题是这样的:当 Question 类没有实现 Parcelable 时,为什么应用只在按 Home 而不是在屏幕方向改变时崩溃?

【问题讨论】:

  • "onSaveInstanceState() 使用 putSerializable() 将 Hashtable 保存到 Bundle 中。" -- 不要将你的数据模型置于实例状态。将您的数据模型放入文件或数据库中。这就是您“防止冗余下载”的方式。 “当我按下模拟器的 Home 键和应用程序无声无息地崩溃时才知道有问题,没有 LogCat 输出。” ——而你对这种无声的、无形的崩溃的证明是……究竟是什么?
  • @CommonsWare:Eclipse 中出现一个弹出窗口,要求打开 Debug 透视图,因为启动已暂停。这就是我知道它崩溃的原因。我已经注释掉了使问题可打包的代码,并且在一次尝试中(在几次尝试中),它确实强制关闭并记录了错误,但在发布此问题之前并没有发生这种情况。
  • 是的,数据确实应该保存到文件中,但这不是我想要做的。我不是在生产应用程序中工作(即使是个人爱好)。我正在修改通过Sam's Teach Yourself Android Application Development in 24 Hours 一书创建的演示应用程序。即使是一本介绍性的书,它似乎也遗漏了一些非常基本的主题。我想看看 onSaveInstanceState() 是如何工作的。我想我现在已经掌握了它的机制,如果不是什么时候以及如何最好地使用它。
  • 如果 Eclipse 拦截到异常,您将不会在 LogCat 中看到堆栈跟踪。让 Eclipse 运行超过异常点,堆栈跟踪应显示在 LogCat 中。
  • 我在序列化 onSaveInstanceState() 中的链表对象时遇到了类似的问题。当我旋转屏幕方向时,对象被序列化和反序列化没有问题。当我点击主页按钮时,应用程序在 onSaveInstanceState 中崩溃,LogCat 报告堆栈溢出错误。有人知道为什么这两种情况会产生不同的结果吗?

标签: android android-activity bundle parcelable activity-lifecycle


【解决方案1】:

据我了解,在配置更改后重新创建活动时,Android 不会序列化实例状态。这就是您的代码有效的原因。持久化对象不需要是可打包的,因为它们只存在于内存中。

这看起来像是一种优化。 Android 知道在这种情况下进程不会被终止,并且不需要将实例状态保存到文件中。 (理论上可以在配置更改过程中终止进程,我真的不知道Android是如何解决这个问题的。

但是当用户按下 Home 键时,您的应用将变为后台。并且它的进程可以在内存不足的情况下终止。 Android 需要将 Activity 的状态保存到文件中,以便将来能够恢复您的应用及其 Activity。在这种情况下,实例状态真正被序列化并保存到持久存储中。这就是为什么您的代码不起作用的原因。

进程终止可能随时发生,因此您不能依赖某些实现细节。只需使实例状态可打包或可序列化,您就不会再遇到这个问题了。

【讨论】:

  • 虽然我自己还没有确认,但这个答案对我来说很有意义(当变量仍然存在于内存中时,为什么要序列化一些东西来改变配置)。这也是迄今为止试图解决由 erichamion 发布的原始问题的唯一答案。因此,我将赏金奖励给这个答案。
【解决方案2】:

引用史蒂夫·莫斯利的话

请注意,根据http://developer.android.com/reference/android/app/Activity.html 中有关活动状态的文档,使用onSaveInstanceStateonRestoreInstanceState安全的。

文档状态(在“活动生命周期”部分):

注意保存很重要 onPause() 中的持久数据 onSaveInstanceState(Bundle) 因为后者不是 生命周期回调,所以不会 如所描述的在每种情况下都被调用 在其文档中。

换句话说,请将您的保存/恢复代码放在onPause()onResume() 中!

【讨论】:

  • 这更适合作为对原始问题的评论,因为它不是对发布的问题的回答:“......为什么应用程序仅在按下 Home 而不是在屏幕方向变化?”
【解决方案3】:

应用没有崩溃。当用户单击 Home 键时,它就被简单地关闭了。这就是 LogCat 没有输出的原因。

在 Activity.onDestroy() 中设置断点来确认这一点。如果我是对的 onDestroy() 将被调用,但 onSaveInstanceState() 不会,因为 onSaveInstanceState() 仅在应用程序处于后台状态时调用,而不是在关闭时调用。

如果您需要在关闭时保存应用程序状态,请将代码放入 onDestroy() 并将其保存到比 Bundle 更持久的东西。

巴里

【讨论】:

    猜你喜欢
    • 2018-08-05
    • 1970-01-01
    • 2019-05-27
    • 1970-01-01
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-18
    相关资源
    最近更新 更多