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,
  private val titles: MutableList
) : 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,
  private val titles: MutableList
) : 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()

}

你可能感兴趣的:(AndroidX的Fragment懒加载)