首发公众号:黑客五六七
作者:贤榆的榆
如果喜欢,请关注、赞赏、点在看
阅读时间:1738字 6分钟
Fragment的懒加载,通常都是在一个Activity中通过ViewPager管理了多个Fragment界面时,会用到的一种模式。当我们每个Fragment都很复杂的时候,为了保证整个Activity的流畅度,我们通常会将第一个Fragment先加载出来,后面的Fragment在其可见时再加载。每当我们想到Fragment懒加载的时候通常都是使用setUserVisiable()方法配合onViewCreated方法来做懒加载,但是你会发现当使用ViewPager2+Fragment的时候,它就并不起作用了。由于种种的历史原因,现在的Fragment嵌套的方式比较多了,所以这里研究一下这些嵌套方式,看看是否能够找到一个合适方式懒加载方式作为我这个BaseLazyFragment的实现,同时兼容这几种嵌套方式。
当然,我的出发点是仍是从生命周期开始探索。这里先温故一下Fragment的生命周期吧:
为了能够更好的观察几种嵌套方式在滑动过程中的引用,除了上面的生命周期之外,我还在日志中打印了onViewCreated和setUserVisibleHint()方法的调用时机。下面看一下我的日志代码:
class LifeCycleFragment : Fragment() {
companion object {
fun create(position: Int): LifeCycleFragment {
val fragment = LifeCycleFragment()
fragment.arguments = bundleOf(Pair("p", position))
return fragment
}
}
var position: String = ""
override fun onAttach(context: Context) {
position = arguments?.getInt("p").toString()
super.onAttach(context)
Log.d("ResumeOnly", "Fragment${position}onAttach")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("ResumeOnly", "Fragment${position}onCreate")
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("ResumeOnly", "Fragment${position}onCreateView")
return TextView(activity).apply {
text = position
layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
textSize = 100f
typeface = Typeface.DEFAULT_BOLD
gravity = Gravity.CENTER
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Log.d("ResumeOnly", "Fragment${position}onActivityCreated")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("ResumeOnly", "Fragment${position}onViewCreated")
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
Log.d("ResumeOnly", "Fragment${position}isVisibleToUser$isVisibleToUser")
}
override fun onStart() {
super.onStart()
Log.d("ResumeOnly", "Fragment${position}onStart")
}
override fun onResume() {
super.onResume()
Log.d("ResumeOnly", "Fragment${position}onResume")
}
override fun onPause() {
super.onPause()
Log.d("ResumeOnly", "Fragment${position}onPause")
}
override fun onStop() {
super.onStop()
Log.d("ResumeOnly", "Fragment${position}onStop")
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("ResumeOnly", "Fragment${position}onDestroyView")
}
override fun onDestroy() {
super.onDestroy()
Log.d("ResumeOnly", "Fragment${position}onDestroy")
}
}
第一种嵌套:ViewPager+FragmentPagerAdapter(fa,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)的生命周期
通过上面的Log日志,其实不难看出通过ViewPager+FragmentPagerAdapter(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)进行管理Fragment,在默认没有设置offscreenPageLimit的值时,也会帮我们多预加载一个,而预加载的这个是走到了onStart方法,当左滑时,原来预加载的这个会走onResume,同时再预加载下一个。很明显我们的lazyLoad方法可以放到onResume去做。当我们滑动到当前页的时候走当前页的onResume方法然后再onResume中调用lazyLoad方法。
第二种嵌套:ViewPager+FragmentPagerAdapter(fm)
根据上面的日志可以看到,再进入当前页面的时候和第一种一样,在默认没有设置offscreenPageLimit的值时,也会帮我们多预加载一个Fragment,但不同的时候,两个Fragment的生命周期都会直接走到onResume的生命周期,并且它们都多了一个isVisibleToUser布尔值来控制Fragment是否可见。
第三种嵌套:ViewPager2+Fragment
根据上面的log可以看到,进入界面时它只会初始化当前的Activity,它的可见与否也是由onResume和onPause进行回调的。它的生命周期就和第一种很像了,只是在没有设置offscreenPageLimit的情况下,它不会去预加载下一个的生命周期。所以我们仍然可以通过onResume来控制懒加载。
制作一个通用的BaseLazyFragment
通过上面的三种嵌套的日志。我们只需要最后确定一件事:就是mUserVisibleHint的默认值
如我们所料,至此我们只需要在onResume和setUserVisibleHint方法中都调用一个lazyLoad方法,并在lazyLoad中去判断mUserVisibleHint的值以及自定义的一个loaded的值即可,另外需要注意的一点是setUserVisibleHint的方法调用的时机可能并不在Fragment的生命周期内
This method may be called outside of the fragment lifecycle.
and thus has no ordering guarantees with regard to fragment lifecycle method calls
所以我们还需要判断根布局是否为null,最后的实现代码如下:
abstract class BaseLazyFragment : Fragment() {
private var cacheView: View? = null
private var loaded: Boolean = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (cacheView == null) {
cacheView = inflater.inflate(layoutId(), container, false)
}
return cacheView
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
lazyLoad()
}
override fun onResume() {
super.onResume()
lazyLoad()
}
private fun lazyLoad() {
if (userVisibleHint && !loaded&& cacheView != null) {
initView()
initData()
loaded = true
}
}
abstract fun layoutId(): Int
abstract fun initData()
abstract fun initView()
}
最后我们一起看一下这三嵌套模式的一个懒加载实现的效果:
这个代码是在google的ViewPager2的代码基础上新增了三个页面进行日志打印,
代码地址:https://github.com/luorenyu/ExplorLazyFragment