AndroidX的Fragment懒加载

AndroidX之前的Fragment懒加载

在AndroidX之前,Fragment的使用有两种方式:

  • 使用ViewPager+Fragment模式
  • 通过FragmentTransaction控制Fragment的使用

1.AndroidX之前ViewPager模式的懒加载

分析:
由于ViewPager的缓存机制offscreenPageLimit始终>=1,所以ViewPager会缓存布局屏幕外的Fragment>=1个,被缓存的屏幕外的Fragment也会像屏幕可见的Fragment一样执行生命周期到onResume,并且在用户滑动后缓存的Fragment由不可见到可见时并不会调用onResume。根据ViewPager调用Fragment的方法顺序:

setUserVisibleHint->onAttach->onCreate->onCreateView->onViewCreated->onActivityCreated->onStart->onResume

所以,在androidX之前,ViewPager模式的懒加载方式是:

  • 对于第一个可见的Fragment,在onResume方法中执行懒加载请求数据
  • 对于缓存的Fragment由不可见到可见时,通过setUserVisibleHint方法中执行懒加载请求数据
abstract class LazyFragment  {

  // Fragment的数据是否加载过
  private var isLoaded = false

  // Fragment是否可见
  private var isVisibleToUser = false

  // 是否加载过onResume
  private var isCallResume = false

  override fun onResume() {
      super.onResume()
      isCallResume = true
      checkLazy()
  }

  private fun checkLazy() {
      if (!isLoaded && isVisibleToUser && isCallResume) {
          lazyInit()
          isLoaded = true
      }
  }

  override fun setUserVisibleHint(isVisibleToUser: Boolean) {
      super.setUserVisibleHint(isVisibleToUser)
      this.isVisibleToUser = isVisibleToUser
      checkLazy()
  }

  override fun onDestroyView() {
      super.onDestroyView()
      isLoaded = false
      isVisibleToUser = false
      isCallResume = false
  }

  abstract fun lazyInit()

}

需要注意的是ViewPager嵌套加载,对于第二个加载的ViewPager的数据绑定一定要放在上一个Fragment的lazyInit懒加载方法中进行初始化。如下:

override fun lazyInit() {
      view_pager?.apply {
          adapter = FragmentLazyStatePageAdapter(
              childFragmentManager,
              generateTextFragments(4),
              generateTextFragmentTitles(4)
          )
      }
      tab_layout?.setupWithViewPager(view_pager)
  }

2.AndroidX之前FragmentTransaction控制模式的懒加载

通过FrameLayout绑定Fragment时,我们要通过FragmentTransaction进行控制。

      FragmentTransaction transaction = FragmentManager.beginTransaction();
      // Fragment绑定FrameLayout
      transaction.add(containerViewId, fragment, fgTag);
      // 控制Fragment的显示/隐藏
      transaction.show(fragment);
      transaction.hide(fragment); 
      // 提交
      transaction.commit();

对于一个FrameLayout绑定多个Fragment,并每次只显示一个,我们可以这样通过FragmentTransaction控制Fragment的行为,代码如下:

// FrameLayout绑定多个Fragment
private fun loadFragmentsTransaction(
  @IdRes containerViewId: Int, showPosition: Int,
  fragmentManager: FragmentManager, vararg fragments: Fragment) {
  if (fragments.isNotEmpty()) {
      fragmentManager.beginTransaction().apply {
          for (index in fragments.indices) {
              val fragment = fragments[index]
              add(containerViewId, fragment, fragment.javaClass.name)
              if (showPosition != index) {
                  hide(fragment)
              } 
          }
      }.commit()
  } else {
      throw IllegalStateException(
          "fragments must not empty"
      )
  }
}

// 控制Fragment的显示/隐藏
private fun showHideFragmentTransaction(fragmentManager: FragmentManager, showFragment: Fragment) {
  fragmentManager.beginTransaction().apply {
      show(showFragment)

      //获取其中所有的fragment,其他的fragment进行隐藏
      val fragments = fragmentManager.fragments
      for (fragment in fragments) {
          if (fragment != showFragment) {
              hide(fragment)
          }
      }
  }.commit()

FragmentTransaction模式下Fragment会全部执行生命周期至onResume,并且会调用onHiddenChanged表示Fragment隐藏是否发生改变,但是第一个可见的Fragment因为是可见的,所以并不会调用onHiddenChanged

所以,anroidX之前FragmentTransaction模式下的懒加载:

  • 第一个可见Fragment通过onResume方法和isHidden变量进行判断进行懒加载。
  • 其它由不可见到可见的Fragment,因为已经执行了onResume方法,所以通过onHiddenChanged方法进行懒加载:
abstract class LazyFragment {

  // Fragment的数据是否加载过
  private var isLoaded = false

  // Fragment是否可见
  private var isVisibleToUser = false

  // 是否加载过onResume
  private var isCallResume = false

  // 是否调用过setUserVisibleHint,表明是ViewPager模式
  private var isCallUserVisibleHint = false

  override fun onResume() {
      super.onResume()
      isCallResume = true
      if (!isCallUserVisibleHint) {
          // 不是ViewPager模式
          isVisibleToUser = !isHidden
      }
      checkLazy()
  }

  private fun checkLazy() {
      if (!isLoaded && isVisibleToUser && isCallResume) {
          lazyInit()
          isLoaded = true
      }
  }

  override fun onHiddenChanged(hidden: Boolean) {
      super.onHiddenChanged(hidden)
      isVisibleToUser = !hidden
      checkLazy()
  }

  override fun onDestroyView() {
      super.onDestroyView()
      isLoaded = false
      isVisibleToUser = false
      isCallUserVisibleHint = false
      isCallResume = false
  }

  abstract fun lazyInit()

}

AndroidX之前的适配ViewPager和FragmentTransaction的懒加载

abstract class LazyFragment : LogFragment() {

  // Fragment的数据是否加载过
  private var isLoaded = false

  // Fragment是否可见
  private var isVisibleToUser = false

  // 是否加载过onResume
  private var isCallResume = false

  // 是否调用过setUserVisibleHint,表明是ViewPager模式
  private var isCallUserVisibleHint = false

  override fun onResume() {
      super.onResume()
      isCallResume = true
      if (!isCallUserVisibleHint) {
          // 不是ViewPager模式
          isVisibleToUser = !isHidden
      }
      checkLazy()
  }

  private fun checkLazy() {
      if (!isLoaded && isVisibleToUser && isCallResume) {
          lazyInit()
          isLoaded = true
      }
  }

  override fun onHiddenChanged(hidden: Boolean) {
      super.onHiddenChanged(hidden)
      isVisibleToUser = !hidden
      checkLazy()
  }

  override fun setUserVisibleHint(isVisibleToUser: Boolean) {
      super.setUserVisibleHint(isVisibleToUser)
      this.isVisibleToUser = isVisibleToUser
      isCallUserVisibleHint = true
      checkLazy()
  }

  override fun onDestroyView() {
      super.onDestroyView()
      isLoaded = false
      isVisibleToUser = false
      isCallUserVisibleHint = false
      isCallResume = false
  }

  abstract fun lazyInit()

}

AndroidX的Fragment懒加载

通过上面的分析我们知道在AndroidX之前,不管Fragment是否可见,都执行了onResume方法,这就违背了生命周期中onResume的设计本意了。所以在安卓X中,在FragmentTransaction类中增加了一个方法setMaxLifecycle(@NonNull Fragment fragment,@NonNull Lifecycle.State state),我们可以通过这个方法的第二个参数设置Fragment最大能执行到哪个生命周期方法。

  • 对于不可见的Fragment,我们可以通过FragmentTransaction. setMaxLifecycle(fragment, Lifecycle.State.STARTED)设置Fragment最大执行到onStart()方法。

  • 对于可见的Fragment,通过FragmentTransaction. setMaxLifecycle(fragment, Lifecycle.State.RESUMED)设置Fragment最大执行到onResume()方法。

AndroidX的ViewPager2+Fragment的懒加载:

ViewPager2加载Fragment使用了新的适配器FragmentStateAdapter,在新的FragmentStateAdapter中已经对Fragment的是否可见对FragmentTransaction. setMaxLifecycle(fragment, Lifecycle.State.STARTED)设置了不同的参数。所以我们可以放心的在onResume方法中进行懒加载:

abstract class LazyXFragment {

  private var isLoaded = false //是否已经加载过数据

  override fun onResume() {
      super.onResume()
      if (!isLoaded) {
          lazyInit()
          isLoaded = true
      }
  }

  override fun onDestroyView() {
      super.onDestroyView()
      isLoaded = false
  }

  abstract fun lazyInit()

}

AndroidX的ViewPager+Fragment的懒加载:

在AndroidX中分别对ViewPager+Fragment的两个适配器FragmentPagerAdapter、FragmentStatePagerAdapter对setMaxLifecycle方法进行适配。只要我们在适配器的第二个构造参数设置为:

  • BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT就表示通过setMaxLifecycle设置执行的最大的生命周期,并不执行setUserVisibleHint方法

  • BEHAVIOR_SET_USER_VISIBLE_HINT则跟AndroidX之前的ViewPager一样,即执行setUserVisibleHint又执行onResume

所以,如果你的项目如果升级到了AndroidX,可以在继承FragmentPagerAdapter、FragmentStatePagerAdapter两个适配器的类的第二个构造参数设置为BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

// 设置FragmentPagerAdapter 适配setMaxLifecycle方法
open class FragmentLazyPagerAdapter(
  fragmentManager: FragmentManager,
  private val fragments: MutableList<Fragment>,
  private val titles: MutableList<String>
) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

  override fun getItem(position: Int): Fragment {
      return fragments[position]
  }

  override fun getCount(): Int {
      return fragments.size
  }

  override fun getPageTitle(position: Int): CharSequence? {
      return titles[position]
  }

}

// 设置FragmentStatePagerAdapter 适配setMaxLifecycle方法
open class FragmentLazyStatePageAdapter(
  fragmentManager: FragmentManager,
  private val fragments: MutableList<Fragment>,
  private val titles: MutableList<String>
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
  override fun getItem(position: Int): Fragment = fragments[position]

  override fun getCount(): Int = fragments.size

  override fun getPageTitle(position: Int): CharSequence? = titles[position]
}

如果我们第二个构造参数不设置,就会默认为BEHAVIOR_SET_USER_VISIBLE_HINT则做旧模式那套。

所以,在AndroidX中,分别对ViewPager+Fragmen两个适配器FragmentPagerAdapter、FragmentStatePagerAdapter对setMaxLifecycle方法进行适配,那么其懒加载和ViewPager2一样:

abstract class LazyXFragment {

private var isLoaded = false //是否已经加载过数据

override fun onResume() {
    super.onResume()
    if (!isLoaded) {
        lazyInit()
        isLoaded = true
    }
}

override fun onDestroyView() {
    super.onDestroyView()
    isLoaded = false
}

abstract fun lazyInit()

}

AndroidX的FragmentTransaction模式的懒加载:

我们可以通过FragmentTransaction对是否可见的Fragment进行设置setMaxLifecycle方法:
AndroidX之前增加,显示/隐藏是这样写的:

// FrameLayout绑定多个Fragment
private fun loadFragmentsTransaction(
@IdRes containerViewId: Int, showPosition: Int,
fragmentManager: FragmentManager, vararg fragments: Fragment) {
if (fragments.isNotEmpty()) {
    fragmentManager.beginTransaction().apply {
        for (index in fragments.indices) {
            val fragment = fragments[index]
            add(containerViewId, fragment, fragment.javaClass.name)
            if (showPosition != index) {
                hide(fragment)
            } 
        }
    }.commit()
} else {
    throw IllegalStateException(
        "fragments must not empty"
    )
}
}

// 控制Fragment的显示/隐藏
private fun showHideFragmentTransaction(fragmentManager: FragmentManager, showFragment: Fragment) {
fragmentManager.beginTransaction().apply {
    show(showFragment)

    //获取其中所有的fragment,其他的fragment进行隐藏
    val fragments = fragmentManager.fragments
    for (fragment in fragments) {
        if (fragment != showFragment) {
            hide(fragment)
        }
    }
}.commit()

在AndroidX中,可以对其进行改进,对于可见的Fragment设置其最大生命周期为onResume,对不可见的Fragment设置最大生命周期为onStart。

// FrameLayout绑定多个Fragment
private fun loadFragmentsTransaction(
  @IdRes containerViewId: Int, showPosition: Int,
  fragmentManager: FragmentManager, vararg fragments: Fragment) {

  if (fragments.isNotEmpty()) {
      fragmentManager.beginTransaction().apply {
          for (index in fragments.indices) {
              val fragment = fragments[index]
              add(containerViewId, fragment, fragment.javaClass.name)
              // 通过setMaxLifecycle设置最大生命周期
              if (showPosition == index) {
                   setMaxLifecycle(fragment, Lifecycle.State.RESUMED)
              } else {
                  hide(fragment)
                   setMaxLifecycle(fragment, Lifecycle.State.STARTED)
              }
          }
      }.commit()
  } else {
      throw IllegalStateException(
          "fragments must not empty"
      )
  }
}

// 控制Fragment的显示/隐藏
private fun showHideFragmentTransaction(fragmentManager: FragmentManager, showFragment: Fragment) {
  fragmentManager.beginTransaction().apply {
      show(showFragment)
       // 对可见的Fragment设置最大生命周期
       setMaxLifecycle(showFragment, Lifecycle.State.RESUMED)

      //获取其中所有的fragment,其他的fragment进行隐藏
      val fragments = fragmentManager.fragments
      for (fragment in fragments) {
          if (fragment != showFragment) {
              hide(fragment)
               setMaxLifecycle(fragment, Lifecycle.State.STARTED)
          }
      }
  }.commit()

但测试得到Fragment+Fragment嵌套时,依然会调用不可见Fregment的onResume方法,所以FragmentTransaction模式不能但但只靠onResume判断还要加上isHidden

abstract class LazyXFragment : LogFragment() {

  private var isLoaded = false //是否已经加载过数据

  override fun onResume() {
      super.onResume()
      if (!isLoaded && !isHidden) {
          lazyInit()
          isLoaded = true
      }
  }

  override fun onDestroyView() {
      super.onDestroyView()
      isLoaded = false
  }

  abstract fun lazyInit()

}

AndroidX的ViewPager2、ViewPager、FragmentTransaction懒加载

提示:ViewPager的适配器和FragmentTransaction要适配setMaxLifecycle方法。

abstract class LazyXFragment : LogFragment() {

private var isLoaded = false //是否已经加载过数据

override fun onResume() {
    super.onResume()
    if (!isLoaded && !isHidden) {
        lazyInit()
        isLoaded = true
    }
}

override fun onDestroyView() {
    super.onDestroyView()
    isLoaded = false
}

abstract fun lazyInit()

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

推荐阅读更多精彩内容