美文网首页经典问题方案知识点
AndroidX的Fragment懒加载

AndroidX的Fragment懒加载

作者: isLJli | 来源:发表于2021-03-08 08:53 被阅读0次

    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()
    
    }
    

    相关文章

      网友评论

        本文标题:AndroidX的Fragment懒加载

        本文链接:https://www.haomeiwen.com/subject/jlsyfltx.html