最近客户反馈几个ViewPager中带Fragment无法感知生命周期的问题。所以对v4包对源码分析一下。现在把结果分享一下。
很多开发者在开发ViewPager的时候,都会带上预加载功能。常见有FragmentPagerAdapter与FragmentStatePagerAdapter配合ViewPager使用,或者完全自定义实现PagerAdapter。ViewPager的预加载功能会把左右相邻Fragment缓存起来,因此Fragment的生命周期在预加载的时候就会被提前调用,或者没有调用。因此GrowingIO 无法trace目标Fragment。
所以解决GrowingIO无法track Fragment问题首先了解ViewPager如何配合PagerAdapter对多个Fragment进行管理。先从ViewPager预加载相邻Fragment开始。具体逻辑见如下源码:
上图的逻辑是预加载左边Fragment的代码,位于ViewPager.pupolate()函数中,其中1代码区代码含义是若当前缓存Fragment宽度不合适或位置不对,调用destoryItem销毁目标Fragment。3代码区是若目标Fragment不存在调用addNewItem添加新的Fragment。
预初始化Fragment逻辑在addNewItem中,源码如下:
见上图代码,addNewItem通过Adapter.instantiateItem函数初始化Fragment。
右边相邻的Fragment同理,所以略。
这就是为什么预加载相邻Fragment时,生命周期会被提前调用对原因。
下面我们来看,显示当前页是怎样显示的。
当前也的显示与更新是在主要依赖PageAdapter页面。setPrimaryItem是显示当前页面,finishUpdate是页面更新结束。
由上面对ViewPager源码分析,我们清楚知道ViewPager通过PagerAdapter实现对Fragment对管理。PagerAdapter关键函数有:
instantiateItem: 根据位置,初始化列表对应Fragment。
setPrimaryItem:根据当前位置与当前Fragment对象,显示当前Fragment。
finishUpdate:完成当前显示Fragment页面刷新。
destroyItem:销毁传入对Fragment对象。
那问题来了,如何识别正在显示对Fragment 与预加载Fragment?
答案是:Fragment.setUserVisibleHint.
官方文档对该函数对解释是:告知系统当前Fragment是否对用户显示。
所以在setPrimaryItem的实现代码中应该调用setUserVisibleHint(true),在instantiateItem的实现代码中,应该调用setUserVisibleHint(false)。v4提供的FragmentStatePagerAdapter、FragmentPageAdapter已经实现该调用,最好直接调用。
规范PageAdapter的写法后,无缝接入GrowingIO无埋点SDK,帮助你的应用增长吧。
关键词:Android、GrowingIO、ViewPager、Fragment、setUserVisibleHint、预加载。