1. 背景知识
ViewPager+Fragment实现滑动标签页时,默认情况下,Fragment的生命周期是由FragmentPagerAdapter和Activity共同管理的。
(1) Activity维持在onResume状态时,Fragment的生命周期主要由FragmentPagerAdapter进行管理。进行了预加载的Fragment会处于onResume状态(即使没有变成可操作),预加载多少个Fragment,可以通过 ViewPager::setOffscreenPageLimit()函数进行设置。 在切换滑动标签页时,已经预加载的Fragment的生命周期不会被触发改变,仍然是onResume。 (2) 当Activity的生命周期发生变化时,所有已经预加载的Fragment的生命周期与Activity保持一致。 Fragment生命周期与Activity生命周期的关系(主要看onResume和onPause): onResume执行顺序 : Activity::onResume() -> Fragment::onResume. (Synchronously) onPause执行顺序 : Fragment::onPause -> Activity::onPause. (Synchronously)2.项目需求
在我们的项目中,因为既需要同时加载多个Fragment, 又需要在Fragment变为可见时做一些事情,不可见时又做一些事情(如切换到该标签页时进行视频播放,退出该标签页时关闭播放),因此,继承Fragment,我们实现自己的MyFragment,在MyFragment中增加了另外两个生命周期: onEnter() (fragment变为可见时回调), onExit()(fragment变为不可见时回调)。这样,我们就可以在onEnter中做开始播放的操作,在onExit中停止播放。
3.解决方案根据需求中的描述,增加了两个生命周期后,完整的Fragment的生命周期就变为:
onCreate()->onCreateView()->onStart()->onResume()->onEnter()->onExit()->onPause()->onStop()->onDestroyView()->onDestroy(). 为了确保生命周期的顺序,需要继承FragmentStatePagerAdapter实现自己的MyFragmentStatePagerAdapter, 重写setPrimaryItem()函数来控制Fragment的生命周期。实现如下: MyFragmentStatePagerAdapter::setPrimaryItem(ViewGroup container, int position, Object object) { MyFragment myFragment = (MyFragment)object; if ((myFragment != NULL) && myFragment.isResumed()) { if (myFragment != mpCurrentPrimaryItem) { if (mpCurrentPrimaryItem) { mpCurrentPrimaryItem.onExit(); mpCurrentPrimaryItem = NULL; } if (myFragment) { myFragment.onEnter(); mpCurrentPrimaryItem = myFragment; } } } }
- MyFragment::onEnter会在几种情况下被执行:
(1) 当Activity是第一次启动时,进入到onResume, 调用ViewPager::setAdapter()函数第一次给ViewPager设置MyFragmentStatePagerAdapter时, 会回调到setPrimaryItem(), 从而使MyFragment进入onEnter.
(2) 若Activity不是第一次进入onResume, 就不能通过ViewPager::setAdapter使fragment进入onEnter了。这时需要在Activity::onResume中给MyFragment设置一个需要进入onEnter的标记,在MyFragment随后进入onResume时(背景知识有说明),找到MyFragment所在的ViewPager,并获取Adapter, 然后调用setPrimaryItem()使自己进入onEnter.(3) 切换页面时,由MyFragmentStatePagerAdapter::setPrimaryItem()执行。- MyFragment::onExit会在两种情况下被执行:
(1) 一种是在Activity执行onPause之后,MyFragmentStatePagerAdapter::setNonePrimaryItem()使MyFragment执行onExit().
MyFragmentStatePagerAdapter::setNonePrimaryItem() { if (mpCurrentPrimaryItem) { mpCurrentPrimaryItem.onExit(); mpCurrentPrimaryItem = NULL; }}需要说明的一点是,由于在Activity onPause之前,已经加载的Fragment会先执行onPause, 所以这种情况下不能确保onExit在onPause之前,这时MyFragmnet生命周期为:onResume()->onEnter()->onPause()->onExit()->onStop(). (这点在我们项目中没有影响)(2) 第二种是在切换页面时,由MyFragmentStatePagerAdapter::setPrimaryItem()实现。