为什么在 onSaveInstanceState() 中保存非 Parcelable 对象的哈希表有时会起作用?
在阅读了一本介绍性 Android 编程书籍后,我想更改示例应用程序,以巩固我对一些未真正涵盖的主题的理解。在进行更改时,我犯了一个错误,但我很好奇为什么该错误在某些情况下起作用,但在其他情况下不起作用。
应用程序中的活动将一系列问题存储在 Hashtable
中,其中 Question 是一个包含一个 int 和两个 String 的小类。正如最初编写的那样,该活动在每次 onCreate()
上从服务器下载问题,因此我想实现 onSaveInstanceState()
以防止一些冗余下载。 onSaveInstanceState()
使用 putSerialized()
将 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 或如何实现 Parcelable 之前,它就可以完美地适应屏幕方向的变化。当我按下模拟器的 home 键并且应用程序默默地、无形地崩溃且没有 LogCat 输出时,我才知道出现了问题。堆栈跟踪引导我查找 Parcelable 并使 Question 实现它。
我的问题不是我做错了什么。问题是这样的:当 Question 类没有实现 Parcelable 时,为什么应用程序仅在按 Home 键而不是在屏幕方向更改时崩溃?
After going through an introductory Android programming book, I wanted to alter the example application in order to solidify my understanding of some topics that weren't really covered. In making the change, I made an error, but I'm curious why the error worked in some cases but not in others.
An activity within the application stores a series of questions in a Hashtable<Integer, Question>
, where Question is a small class holding an int and two Strings. As originally written, the activity downloads the questions from a server on every onCreate()
, so I wanted to implement onSaveInstanceState()
to prevent some redundant downloads. onSaveInstanceState()
saves the Hashtable into the Bundle using putSerializable()
.
@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);
}
}
It worked perfectly for screen orientation changes even before I knew what a Parcelable was or how to implement one. I only knew there was a problem when I pressed the emulator's home key and the app silently, invisibly crashed with no LogCat output. The stack trace led me to look up Parcelable and make Question implement it.
My question isn't what I did wrong. The question is this: When the Question class did not implement Parcelable, why did the app crash only on pressing Home and not on a screen orientation change?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
据我了解,在配置更改后重新创建活动时,Android 不会序列化实例状态。这就是你的代码有效的原因。持久化对象不需要可分割,因为它们仅存在于内存中。
这看起来像是一种优化。 Android 知道在这种情况下进程不会终止,因此无需将实例状态保存到文件中。 (理论上这个进程可以在配置更改期间终止,我真的不知道Android是如何解决这个问题的)。
但是,当用户按 Home 键时,您的应用程序将变为后台。并且在内存不足的情况下可以终止其进程。 Android 需要将 Activity 的状态保存到文件中,以便将来能够恢复您的应用程序及其 Activity。在这种情况下,实例状态实际上被序列化并保存到持久存储中。这就是你的代码不起作用的原因。
进程终止可能随时发生,因此您不能依赖某些实现细节。只需使实例状态可分割或可序列化,您就不会再遇到此问题。
As far as I understand Android doesn't serialize an instance state when recreating an activity after configuration changes. That's why your code works. Persisted objects just don't need to be parcelable because they exist in memory only.
This looks like an optimization. Android knows that the process will not be terminated in this case and there's no need to save the instance state to a file. (In theory the process can be terminated during the configuration change and I don't really know how Android solves this problem).
But when the user presses Home key your app becomes background. And its process can be terminated in case of low memory. Android needs to save activity's state to a file in order to be able to restore your app and its activities in future. In this case the instance state is really serialized and saved to a persistent storage. And that's why your code doesn't work.
Process termination can occur at any moment so you can't rely on some implementation details. Just make instance state parcelable or serializable and you will not face this problem again.
请注意,根据 Activity 状态文档,使用
onSaveInstanceState
和onRestoreInstanceState
是不安全的在 http://developer.android.com/reference/android/app/Activity。 html。该文件指出(在“活动生命周期”部分):
换句话说,请将保存/恢复代码放在
onPause()
和onResume()
中!Note that it is NOT safe to use
onSaveInstanceState
andonRestoreInstanceState
, according to the documentation on Activity states in http://developer.android.com/reference/android/app/Activity.html.The document states (in the 'Activity Lifecycle' section):
In other words, put your save/restore code in
onPause()
andonResume()
instead!该应用程序没有崩溃。当用户单击 Home 键时,它就会被关闭。这就是 LogCat 没有输出的原因。
在 Activity.onDestroy() 中设置断点来确认这一点。如果我是对的, onDestroy() 将被调用,但 onSaveInstanceState() 不会,因为 onSaveInstanceState() 仅在应用程序置于后台状态时调用,而不是在应用程序关闭时调用。
如果您需要在关闭时保存应用程序状态,请将代码放在 onDestroy() 中并将其保存到比 Bundle 更持久的东西中。
巴里
The app did not crash. It was simply shut down when the user clicked the Home key. That's why there was no output to LogCat.
Set a breakpoint in Activity.onDestroy() to confirm this. If I'm right onDestroy() will be called but onSaveInstanceState() will not, because onSaveInstanceState() is only called when the app is placed in the background state, not when it's shut down.
If you need to save the app state upon shutdown, place the code in onDestroy() and save it to something more persistent than a Bundle.
Barry