第 14 章 fragment 的保留

请参考教材,全面理解和完成本章节内容... ...

复制工程ch13,将工程目录改名为ch14。

当前,HelloMoon应用对设备旋转的处理还不够完善。运行HelloMoon应用,播放音频,然后旋转设备。音频播放会嘎然而止。

设备旋转后,HelloMoonActivity随即被销毁。与此同时,负责销毁HelloMoonFragment的FragmentManager立即逐一调用fragment的生命周期方法,即onPause()、onStop()和OnDestroy()方法。我们知道,HelloMoonFragment.onDestroy()方法被调用后,MediaPlayer实例即被释放,结果导致了音频播放的停止。

在本书的第3章,我们通过覆盖Activity.onSaveInstanceState(Bundle)方法,修复了GeoQuiz应用的设备旋转相关问题。设备旋转后,新产生的activity读取保存的数据,然后恢复到旋转前的状态。Fragment具有相同功能的onSaveInstanceState(Bundle)方法。然而,就算保存了MediaPlayer对象的状态并在随后恢复,音频播放仍会中断。这显然会惹恼用户。

14.1 保留 fragment 实例

幸运的是,为应对设备配置的变化,可使用fragment的一个特殊方法来确保MediaPlayer实例一直存在。覆盖HelloMoonFragment.onCreate()方法并设置fragment的属性值,如代码清单14-1所示。

代码清单14-1 调用setRetainInstance(true)方法(HelloMoonFragment.java)

fragment的retainInstance属性值默认为false。这表明其不会被保留。因此,设备旋转时fragment会随托管activity一起销毁并重建。调用setRetainInstance(true)方法可保留fragment。已保留的fragment不会随activity一起被销毁。相反,它会被一直保留并在需要时原封不动的传递给新的activity。

对于已保留的fragment实例,其全部实例变量(如mPlayButton、MPlayer和mStopButton)值也将保持不变,因此可放心继续使用。

运行HelloMoon应用。播放音频,然后旋转设备,可看到音频的播放丝毫未受影响。

14.2 设备旋转与保留的 fragment

我们来看看保留的fragment的工作原理。保留的fragment利用了这样一个事实:可销毁和重建fragment的视图,但无需销毁fragment自身。

设备配置发生改变时,FragmentManager首先销毁队列中的fragment的视图。在设备配置改变时,总是销毁与重建fragment与activity的视图,都是基于同样的理由:新的配置可能需要新的资源来匹配;当有更合适的匹配资源可以利用时,则需重新创建视图。

紧接着,FragmentManager检查每个fragment的retainInstance属性值。如属性值为false(初始默认值),FragmentManager会立即销毁该fragment实例。随后,为适应新的设备配置,新activity的新FragmentManager会创建一个新的fragment及其视图,如图14-1所示。

图14-1 设备旋转与默认不保留的UI fragment

如属性值为true,则该fragment的视图立即被销毁,但fragment本身不会被销毁。为适应新的设备配置,当新的activity创建后,新的FragmentManager会找到被保留的fragment,并重新创建它的视图,如图14-2所示。

图14-2 设备旋转与保留的UI fragment

虽然保留的fragment没有被销毁,但它已脱离消亡中的activity并处于保留状态。尽管此时的fragment仍然存在,但已没有任何activity在托管它。

图14-3 fragment的生命周期

fragment必须同时满足两个条件才能进入保留状态:

  • 已调用了fragment的setRetainInstance(true)方法

  • 因设备配置改变(通常为设备旋转),托管activity正在被销毁

Fragment处于保留状态的时间非常短暂,即fragment脱离旧activity到重新附加给立即创建的新activity之间的一段时间。

14.3 保留的 fragment:一切都完美了吗

保留fragment可以说是Android里巧妙的设计,不是吗?没错!它确实给应用开发带来了极大地便利,貌似解决了因设备旋转而销毁activity和fragment所导致的全部问题。当设备配置发生改变时,除了通过创建全新视图获取最合适的资源以外,还可轻松保留原有数据及对象。

为什么不保留每个fragment,或者默认设置fragment的retainInstance属性值为true?这是因为Android似乎并不鼓励保留fragment。我们尚不明确其具体原因,但需要指出的是,如果哪天Android开发团队不再重视fragment的此项特色,保不准什么时候会出问题。

请记住,只有当activity因设备配置发生改变被销毁时,fragment才会短时间处于被保留状态。如果activity是因操作系统需要回收内存而被销毁,则所有被保留的fragment也会被随之销毁。

14.4 设备旋转处理与 onSaveInstanceState(Bundle)方法

onSaveInstanceState(Bundle)方法是用于处理设备旋转问题的另一工具。事实上,如果某个应用不存在任何设备旋转相关问题,这还要归功于onSaveInstanceState(Bundle)方法的默认工作行为。

CriminalIntent应用就是一个很好的例子。CrimeFragment没有被保留,但如果改变某项crime的标题或者切换问题是否解决的状态,则View对象的新状态会被自动保存并在设备旋转后得到恢复。这就是onSaveInstanceState(...)方法的设计用途——保存并恢复应用的UI状态。

覆盖Fragment.onSaveInstanceState(...)方法与保留fragment方法的主要区别在于,数据可以保存多久。如只需短暂保留数据,能应对设备配置改变就可以了,则保留fragment可以很轻松地解决问题。如果是保存对象,则更能体会使用保留fragment的便利。因为我们再也无需操心要保存的对象是否已实现Serializable接口了。

如需持久地保存数据,保留fragment的方式就行不通了。用户暂时离开应用后,如系统因回收内存需要销毁activity,则保留的fragment也会被随之销毁。

为更清楚地了解两种数据保存方式的差异,我们再回头看看GeoQuiz应用。当时我们面临的问题是,一旦设备发生旋转,数组中题目的索引值即被重置为零。无论用户正在回答哪一道题目,设备旋转后,用户总是会回到第一道题目。保存题目的索引值,­­然后在设备旋转后重新读取该值,以保证用户能够看到正确的题目。

GeoQuiz应用没有使用fragment。不过,假设以QuizActivity托管一个QuizFragment的方式,重新设计GeoQuiz应用。对于覆盖Fragment.onSaveInstanceState(...)方法保存题目索引值,以及通过保留QuizFragment存留变量这两种方式,又该如何选择?

图14-4为三种需处理的不同生命周期:activity对象(包括非保留的fragment)的生命周期,被保留fragment的生命周期以及activity记录的生命周期。

­­­

图14-4 三种不同的生命周期

activity对象的生命周期最短。这也是设备旋转问题的根源。题目索引值保留的时间需长于activity对象的生命周期。

如保留了QuizFragment,则题目索引值存留的时间也就是被保留fragment的生命周期。GeoQuiz应用只包含5道题目,因此选择保留QuizFragment的方式来处理设备旋转问题,相对要容易一些,且所需编写的代码量也不多。只需先初始化题目索引成员变量,然后在QuizFragment.onCreate(...)方法中调用setRetainInstance(true)方法即可,如代码清单14-2所示。

代码清单14-2 保留假想的QuizFragment

通过将题目索引变量同被保留fragment的生命周期绑定同步,则题目索引变量可不受activity对象销毁的影响而保留下来,从而解决了设备旋转导致的索引变量值重置的问题。然而,如图14-4所示,进程关闭时,被保留QuizFragment中的索引变量值也会被销毁。进程的关闭通常可能发生在用户暂时离开应用,系统为回收内存而销毁activity以及保留fragment的时候。

应用只包含五道题目,用户被要求从头来过勉强可以接受。但如果GeoQuiz应用含有100道题目呢?返回应用后发现又要从第一道题目重新开始,用户不疯掉才怪!显然,需将题目索引变量的存留时间与activity记录的生命周期保持同步。因此,应在onSaveInstance State(...)方法中将题目索引变量值保存下来。这样,用户在暂时离开应用再返回时,仍可接上题继续开始答题。

因此,如果activity或fragment中有需要长久保存的东西,则应覆盖onSaveInstanceState(Bundle)方法,将其状态保存下来。这样,由于同activity记录的生命周期保持了同步,后续可在需要时对其进行恢复。

时间: 2024-08-15 04:40:56

第 14 章 fragment 的保留的相关文章

第14章 启动文件详解—零死角玩转STM32-F429系列

第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料<STM32F4xx 中文参考手册>第十章-中断和事件:表 46. STM32F42xxx 和 STM32F43xxx 的向量表:MDK中的帮助手册—ARM Development Tools:用来查询ARM的汇编指令和编译器相关的指令. 14.1 启动文件简介 启动文件由汇编编写,是

敏捷软件开发:原则、模式与实践——第14章 使用UML

第14章 使用UML 在探索UML的细节之前,我们应该先讲讲何时以及为何使用它.UML的误用和滥用已经对软件项目造成了太多的危害. 14.1 为什么建模 建模就是为了弄清楚某些东西是否可行.当模型比要构建的真实实体便宜很多时,我们就会使用模型来研究设计. 14.1.1 为什么构建软件模型 当我们有一些确定的东西需要测试,并且使用UML要比使用代码测试的代价更低一些是,就使用UML.比如,我有一个关于某个设计的想法.我想知道团队中的其他开发人员是否认为它是一个好的想法,于是,我就在白板上画一幅UM

第14章 启动文件详解

第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料<STM32F4xx 中文参考手册>第十章-中断和事件:表 46. STM32F42xxx 和 STM32F43xxx 的向量表:MDK中的帮助手册-ARM Development Tools:用来查询ARM的汇编指令和编译器相关的指令. 14.1 启动文件简介 启动文件由汇编编写,是

【二代示波器教程】第14章 uCOS-III操作系统版本二代示波器实现

第14章      uCOS-III操作系统版本二代示波器实现 本章教程为大家讲解uCOS-III操作系统版本的二代示波器实现.主要讲解RTOS设计框架,即各个任务实现的功能,任务间的通信方案选择,任务栈,系统栈以及全局变量共享问题.同时,工程调试方法也专门做了说明. 14.1  注意事项(重要必读) 14.2  任务功能划分 14.3  用户任务优先级设置 14.4  全局变量分配,系统堆栈和任务堆栈 14.5  任务间通信和全局变量共享问题 14.6  uCOS-III系统调试 14.7  

Windows核心编程:第14章 探索虚拟内存

Github https://github.com/gongluck/Windows-Core-Program.git //第14章 探索虚拟内存.cpp: 定义应用程序的入口点. // #include "stdafx.h" #include "第14章 探索虚拟内存.h" int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lp

第14章、组织需要怎样的乐观

目录 第14章.组织需要怎样的乐观 职场中的乐观 如何在工作中变得乐观 反驳自己的想法 跳墙游戏 反驳 开始ABCDE练习 拯救坏心情 证据 其他可能性 暗示 用处 练习.练习.再练习 乐观箴言 第14章.组织需要怎样的乐观 史蒂芬是一位保险业务员,每天晚上5点半到9点半之间,他必须给那些他不认识的人打电话,推销保险.他最恨这个工作.他从芝加哥地区最近出生的婴儿名单中获知他们父母的名字,然后打电话给他们.他的晚上通常是这样度过的: 第1个人在听他说话15秒后就把电话挂掉了.第2个人告诉他,她已经

《TCP/IP详解卷1:协议》第14章 DNS:域名系统---读书笔记

<TCP/IP详解卷1:协议>第14章 DNS:域名系统---读书笔记 1.引言 5.指针查询 DNS中一直难于理解的部分就是指针查询方式,即给定一个IP地址,返回与该地址对应的域名. 当一个组织加入Internet,并获得DNS域名空间的授权,如noao.edu,则它们也获得了对应IP地址的in-addr.arpa域名空间的授权.在noao.edu这个例子中,它的网络号是140.252的B类网络.在DNS树中结点in-addr.arpa的下一级必须是该IP地址的第一字节(例中为140),再下

JavaScript高级程序设计(第三版)学习笔记13、14章

第13章,事件 事件冒泡 IE的事件叫做事件冒泡:由具体到不具体 <!DOCTYPE html> <html> <head> <title>Event Bubbling Example</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html> 如果你单击了<div>

&lt;&lt;精通iOS开发&gt;&gt;第14章例子代码小缺陷的修复

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 首先推荐大家看这本书,整本书逻辑非常清晰,代码如何从无到有,到丰满说的很有条理. 说实话本书到目前为止错误还是极少的,不过人无完人,在第14章前半部分项目的代码中,作者在MasterVC到DetailVC中又直接添加了一个segue,该segue的ID为"masterToDetail",作用是当新建一个tinyPix文档时可以直接跳转到DetailV