上一篇文章里主要讲到Fragment如何创建,如何与Activity交互,基本的API用法等。有兴趣可以先了解下Fragment 用法总结(一)。
Fragment的生命周期大家都熟悉,本篇文章主要从Activity管理Fragment的角度来了解Fragment的生命周期变化,并与Activity的生命周期协调一致。
通常,我们的Fragment至少应实现以下生命周期方法:
其它生命周期方法的作用在下面会讲到。
管理Fragment生命周期与管理 Activity 生命周期很相似。和 Activity 一样,Fragment也以三种状态存在:
恢复
Fragment在运行中的 Activity 中可见。
暂停
另一个 Activity 位于前台并具有焦点,但此Fragment所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。
停止
Fragment不可见。宿主 Activity 已停止,或Fragment已从 Activity 中删除,但已添加到返回栈。 停止Fragment仍然处于活动状态(系统会保留所有状态和成员信息)。 不过,它对用户不再可见,如果 Activity 被终止,它也会被终止。
同样与 Activity 一样,假使 Activity 的进程被终止,而需要在重建 Activity 时恢复Fragment状态,可以使用 Bundle 保留Fragment的状态。可以在Fragment的 onSaveInstanceState() 回调期间保存状态,并可在 onCreate()、onCreateView() 或 onActivityCreated() 期间恢复状态。
Activity 生命周期与Fragment生命周期之间的最显著差异在于它们在其各自返回栈中的存储方式。 默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈(以便用户通过“返回” 按钮回退到Activity)。不过,Fragment只有在Fragment的事务执行期间通过调用 addToBackStack() 显式请求保存实例时,系统才会将Fragment放入由宿主 Activity 管理的返回栈。
在其他方面,管理Fragment生命周期与管理 Activity 生命周期非常相似。 因此,管理 Activity 生命周期的做法同样适用于Fragment。 但还需要了解 Activity 的生命周期对Fragment生命周期的影响。
注意:如需 Fragment 内的某个 Context 对象,可以调用 getActivity()。但要注意,请仅在Fragment附加到 Activity 时调用 getActivity()。如果Fragment尚未附加,或在其生命周期结束期间分离,则 getActivity() 将返回 null,所以getActivity()和getContext()(API level 23 新增方法)要在Fragment回调函数onAttach之后才能调用。
Fragment所在的 Activity 的生命周期会影响Fragment的生命周期,其表现为,Activity 的每次生命周期回调都会引发每个Fragment的类似回调。 例如,当 Activity 收到 onPause() 时,Activity 中的每个Fragment也会收到 onPause()。
不过,Fragment还有几个额外的生命周期回调,用于处理与 Activity 的唯一交互,以执行构建和销毁Fragment UI 等操作。这些额外的回调方法是:
onAttach()
onCreateView()
onActivityCreated()
onDestroyView()
onDetach()
宿主 Activity 影响Fragment的生命周期流。Activity 的每个连续状态如何决定Fragment可以收到的回调方法。 例如,当 Activity 收到其 onCreate() 回调时,Activity 中的Fragment只会收到 onActivityCreated() 回调。
一旦 Activity 达到恢复状态,就可以意向 Activity 添加Fragment和删除其中的Fragment。 因此,只有当 Activity 处于恢复状态时,Fragment的生命周期才能独立变化。
不过,当 Activity 离开恢复状态时,Fragment会在 Activity 的推动下再次经历其生命周期。
我们结合Activity对fragment的操作方法,了解一下不同的操作方法对Fragment的生命周期方法的影响。
下面是动图操作的步骤:
一、fragmentTransaction.add(R.id.content_main, fragmentA).commit();
FragmentA: onAttach;
FragmentA: onCreate;
FragmentA: onCreateView;
FragmentA: onActivityCreated;
FragmentA: onStart;
FragmentA: onResume;
为Activity添加第一个Fragment的时候不要把事物添加到返回栈,如果添加了,则会在“返回”按钮按下后回退到Activity的空白页面,这应该是大家不想看到的。
二、fragmentTransaction.replace(R.id.content_main, fragmentB).addToBackStack(null).commit();
FragmentA: onPause;
FragmentA: onStop;
FragmentA: onDestroyView;
FragmentB: onAttach;
FragmentB: onCreate;
FragmentB: onCreateView;
FragmentB: onActivityCreated;
FragmentB: onStart;
FragmentB: onResume;
FragmentTransaction的replace方法相当于remove方法和add方法的结合,上面的程序输出也证明了这一点
三、fragmentTransaction.hide(fragmentB).add(R.id.content_main, fragmentC).addToBackStack(null).commit();
FragmentC: onAttach;
FragmentC: onCreate;
FragmentC: onCreateView;
FragmentC: onActivityCreated;
FragmentC: onStart;
FragmentC: onResume;
FragmentTransaction的hide方法不触发Fragment的生命周期方法,所以隐藏FragmentB没有触发接下来的生命周期,上面的输出表明这个事物提交后只触发了添加fragmentC的生命周期方法。
四、fragmentTransaction.detach(fragmentC).addToBackStack(null).commit();
FragmentC: onPause;
FragmentC: onStop;
FragmentC: onDestroyView;
将fragmentC detach
五、按下返回键 popBackStack
FragmentC: onCreateView;
FragmentC: onActivityCreated;
FragmentC: onStart;
FragmentC: onResume;
返回操作,恢复到detach FragmentC的状态,FragmentC从detach状态变为attach状态
六、按下返回键 popBackStack FragmentC被移除
FragmentC: onPause;
FragmentC: onStop;
FragmentC: onDestroyView;
FragmentC: onDestroy;
FragmentC: onDetach;
返回操作,恢复到加载FragmentC前的状态,上一个提交的事物为hide->FragmentB
add ->FragmentC
则恢复操作相反 “remove->FragmentC”
“show->FragmentB”
,
上面讲到hide方法不触发Fragment生命周期,同理show方法也不触发Fragment生命周期。
由于FragmentC已经退出”历史舞台”取消与Activity关联,所以执行onDetach
方法。
七、按下返回键 popBackStack FragmentB
FragmentB: onPause;
FragmentB: onStop;
FragmentB: onDestroyView;
FragmentB: onDestroy;
FragmentB: onDetach;
FragmentA: onCreateView;
FragmentA: onActivityCreated;
FragmentA: onStart;
FragmentA: onResume;
返回操作,恢复到加载FragmentB前的状态,则replace的逆操作“remove FragmentB”
“add FragmentA”
八、按下返回键 popBackStack FragmentA
FragmentA: onPause;
FragmentA: onStop;
FragmentA: onDestroyView;
FragmentA: onDestroy;
FragmentA: onDetach;
返回操作,恢复到加载FragmentA前的状态,没有加入回退栈,Activity直接退出。
眼尖的同学肯定发现了,上面动态图片中FragmentA的EditView设置了新内容,但是在回退回来的时候,又恢复到原来的内容。这是因为:如果不给一个View设置一个id
,那么在Activity或者Fragment调用onSaveInstanceState(Bundle outState)
方法时,就没办法保存它的状态,这会导致在恢复界面的时候丢失上次的状态。
hide(A).add(B).addToBackStack(null).commit();
replace(id,B).addToBackStack(null).commit();
detach(A).add(B).addToBackStack(null).commit();
注意:当你移除或替换 Fragment 并向返回堆栈添加事务时,已移除的 Fragment 会停止(而不是销毁)。如果用户向后导航,还原该 Fragment,它会重新启动。如果你没有向返回堆栈添加事务,那么该 Fragment 在移除或替换时就会被销毁。
addToBackStack()
把切换事物加到回退栈里,保证Fragment能成功返回,其次不要每次都new一个新的Fragment对象,保存Fragment实例。这里要注意屏幕横竖切换的时候Fragment会重叠。id
就可以(好像没什么意义)。如果是非应用行为导致Fragment异常退出和恢复,在onSaveInstanceState()里保存状态。
上面动图的示例代码
本篇文章主要从Activity管理Fragment的角度来了解Fragment的生命周期变化,并与Activity的生命周期协调一致。下一篇Fragment 用法总结(三)主要介绍:没有视图的Fragment的用处、使用Fragment创建对话框、如何与ActionBar和MenuItem集成等高级用法。