「Do.034」探索兼容的Fragment懒加载模式

image

首发公众号:黑客五六七
作者:贤榆的榆
如果喜欢,请关注、赞赏、点在看
阅读时间:1738字 6分钟

Fragment的懒加载,通常都是在一个Activity中通过ViewPager管理了多个Fragment界面时,会用到的一种模式。当我们每个Fragment都很复杂的时候,为了保证整个Activity的流畅度,我们通常会将第一个Fragment先加载出来,后面的Fragment在其可见时再加载。每当我们想到Fragment懒加载的时候通常都是使用setUserVisiable()方法配合onViewCreated方法来做懒加载,但是你会发现当使用ViewPager2+Fragment的时候,它就并不起作用了。由于种种的历史原因,现在的Fragment嵌套的方式比较多了,所以这里研究一下这些嵌套方式,看看是否能够找到一个合适方式懒加载方式作为我这个BaseLazyFragment的实现,同时兼容这几种嵌套方式。

当然,我的出发点是仍是从生命周期开始探索。这里先温故一下Fragment的生命周期吧:


image

为了能够更好的观察几种嵌套方式在滑动过程中的引用,除了上面的生命周期之外,我还在日志中打印了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)的生命周期

image

通过上面的Log日志,其实不难看出通过ViewPager+FragmentPagerAdapter(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)进行管理Fragment,在默认没有设置offscreenPageLimit的值时,也会帮我们多预加载一个,而预加载的这个是走到了onStart方法,当左滑时,原来预加载的这个会走onResume,同时再预加载下一个。很明显我们的lazyLoad方法可以放到onResume去做。当我们滑动到当前页的时候走当前页的onResume方法然后再onResume中调用lazyLoad方法。

第二种嵌套:ViewPager+FragmentPagerAdapter(fm)

image

根据上面的日志可以看到,再进入当前页面的时候和第一种一样,在默认没有设置offscreenPageLimit的值时,也会帮我们多预加载一个Fragment,但不同的时候,两个Fragment的生命周期都会直接走到onResume的生命周期,并且它们都多了一个isVisibleToUser布尔值来控制Fragment是否可见。

第三种嵌套:ViewPager2+Fragment

image

根据上面的log可以看到,进入界面时它只会初始化当前的Activity,它的可见与否也是由onResume和onPause进行回调的。它的生命周期就和第一种很像了,只是在没有设置offscreenPageLimit的情况下,它不会去预加载下一个的生命周期。所以我们仍然可以通过onResume来控制懒加载。

制作一个通用的BaseLazyFragment

通过上面的三种嵌套的日志。我们只需要最后确定一件事:就是mUserVisibleHint的默认值


image

如我们所料,至此我们只需要在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()
}

最后我们一起看一下这三嵌套模式的一个懒加载实现的效果:

1588834300301

这个代码是在google的ViewPager2的代码基础上新增了三个页面进行日志打印,
代码地址:https://github.com/luorenyu/ExplorLazyFragment

欢迎关注黑客五六七

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271