[关闭]
@martin0207 2018-12-11T08:46:42.000000Z 字数 4108 阅读 1007

实现Fragment懒加载(一)—— 继承LazyInitFragment

android


镇楼

GIF.gif

前言

开发过程中,不可避免的会遇到ViewPager + Fragment组合。由于ViewPager的机制,会将ViewPager容器中当前Fragment的左右两个Fragment进行初始化,生命周期到达onResume,这时候我们在Fragment写的初始化方法,早已经调用,后面的Fragment没有展示,这时候初始化对用户并不友好。
不仅如此,如果我们还想保持Fragment的存活,使用vp.offscreenPageLimit = mAdapter.count,每个Fragment的初始化再带有个网络请求……爆炸

重点

Fragment类中,含有setUserVisibleHint(boolean isVisibleToUser)方法,该方法由系统自动调用.
我们可以通过getUserVisibleHint()方法获取Fragment当前是否对用户可见
接下来,咱们就围绕该方法对Fragment进行封装。

分析

我们将到onResume之前的生命周期打印一下:

  1. // 第一次创建
  2. E/LazyInitFragment: InheritZeroFragment setUserVisibleHint false
  3. E/LazyInitFragment: InheritZeroFragment setUserVisibleHint true
  4. E/LazyInitFragment: InheritZeroFragment onAttach
  5. E/LazyInitFragment: InheritZeroFragment onCreate
  6. E/LazyInitFragment: InheritZeroFragment onCreateView
  7. E/LazyInitFragment: InheritZeroFragment onActivityCreated
  8. E/LazyInitFragment: InheritZeroFragment onResume
  9. //划出(隐藏)
  10. E/LazyInitFragment: InheritZeroFragment setUserVisibleHint false
  11. //划入(展示)
  12. E/LazyInitFragment: InheritZeroFragment setUserVisibleHint true

可以看出

一不小心分析出了这么多东西,条件差不多了,下面开始冻手

实现

我总觉得,独立的文字写再多,都不如配合代码读来的真切。所以,我将想要说的内容,都放在代码的注释当中。
上面的分析可以不看,请将代码注释看的仔细些。

1. 提取接口

lazyInit()以及Fragment在onResume及之前的生命周期状态值提取出来。

  1. interface ILazyInitFragment {
  2. /**
  3. * fragment的状态
  4. * 延迟初始化一般都在这几种状态下(ATTACH除外)
  5. * 所以只添加了这些状态
  6. */
  7. companion object {
  8. /**
  9. * fragment依附于Activity(默认状态)
  10. * 一般处于该状态下,不做任何操作
  11. */
  12. const val ON_ATTACH = 0
  13. const val ON_CREATED = 1
  14. const val ON_CREATED_VIEW = 2
  15. const val ON_ACTIVITY_CREATED = 3
  16. const val ON_RESUME = 4
  17. }
  18. /**
  19. * 延迟初始化
  20. */
  21. fun lazyInit()
  22. }

之所以费劲提取出来,是因为考虑到,已经成型的项目,替换BaseFragment类很费劲,我希望能够让Fragment只实现一个接口就可以实现这个功能。
当然,后话了。

2. 实现LazyInitFragment

  1. abstract class LazyInitFragment : Fragment(), ILazyInitFragment {
  2. val TAG = this.javaClass.simpleName
  3. /**
  4. * 测试时控制生命周期的日志展示
  5. */
  6. val mShowLog = true
  7. /**
  8. * fragment的当前状态
  9. * 用来控制lazyInit的调用
  10. */
  11. var mCurrentState = ILazyInitFragment.ON_ATTACH
  12. /**
  13. * 调用lazyInit的状态判定
  14. * 即mCurrentState>=mLazyInitState时,调用
  15. */
  16. fun getLazyInitState() = ILazyInitFragment.ON_ACTIVITY_CREATED
  17. /**
  18. * 是否已经初始化
  19. */
  20. var mInitialized = false
  21. /**
  22. * 设置fragment是否对用户可见
  23. * Android自带的方法,且会自动调用
  24. */
  25. override fun setUserVisibleHint(isVisibleToUser: Boolean) {
  26. super.setUserVisibleHint(isVisibleToUser)
  27. if (mShowLog) Timber.e("$TAG setUserVisibleHint $isVisibleToUser")
  28. doLazyInit()
  29. }
  30. override fun onAttach(context: Context?) {
  31. super.onAttach(context)
  32. mCurrentState = ILazyInitFragment.ON_ATTACH
  33. if (mShowLog) Timber.e("$TAG onAttach")
  34. doLazyInit()
  35. }
  36. override fun onCreate(savedInstanceState: Bundle?) {
  37. super.onCreate(savedInstanceState)
  38. mCurrentState = ILazyInitFragment.ON_CREATED
  39. if (mShowLog) Timber.e("$TAG onCreate")
  40. doLazyInit()
  41. }
  42. override fun onActivityCreated(savedInstanceState: Bundle?) {
  43. super.onActivityCreated(savedInstanceState)
  44. mCurrentState = ILazyInitFragment.ON_ACTIVITY_CREATED
  45. if (mShowLog) Timber.e("$TAG onActivityCreated")
  46. doLazyInit()
  47. }
  48. override fun onResume() {
  49. super.onResume()
  50. mCurrentState = ILazyInitFragment.ON_RESUME
  51. if (mShowLog) Timber.e("$TAG onResume")
  52. doLazyInit()
  53. }
  54. private fun doLazyInit() {
  55. /*
  56. 若达到我们设置的状态条件,则调用方法:
  57. 未初始化 且
  58. 用户可见 且
  59. 当前状态>= 懒加载触发状态
  60. */
  61. if (!mInitialized && userVisibleHint && mCurrentState >= getLazyInitState()) {
  62. lazyInit()
  63. mInitialized = true
  64. }
  65. }
  66. override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
  67. if (mShowLog) Timber.e("$TAG onCreateView")
  68. mCurrentState = ILazyInitFragment.ON_CREATED_VIEW
  69. return inflater.inflate(getLayoutRes(), container, false)
  70. }
  71. /**
  72. * 获取fragment的布局Id
  73. */
  74. abstract fun getLayoutRes(): Int
  75. override fun onPause() {
  76. super.onPause()
  77. if (mShowLog) Timber.e("$TAG onPause")
  78. }
  79. override fun onStop() {
  80. super.onStop()
  81. if (mShowLog) Timber.e("$TAG onStop")
  82. }
  83. /**
  84. * 这里是个小坑
  85. * ViewPager的规则,默认会销毁当前Fragment及左右两个之外的Fragment视图。
  86. * 即:滑到第3个Fragment时,第1个Fragment会被销毁视图,且只调用到这一步
  87. */
  88. override fun onDestroyView() {
  89. super.onDestroyView()
  90. mInitialized = false
  91. if (mShowLog) Timber.e("$TAG onDestroyView")
  92. }
  93. override fun onDestroy() {
  94. super.onDestroy()
  95. if (mShowLog) Timber.e("$TAG onDestroy")
  96. }
  97. override fun onDetach() {
  98. super.onDetach()
  99. if (mShowLog) Timber.e("$TAG onDetach")
  100. }
  101. }

后语

  1. 实现方式并不复杂,关键在于setUserVisibleHint这个方法,之前不知道,所以卡了好长时间。
  2. 代码中使用了Timber日志工具,也推荐使用。
  3. 在复制代码使用时,最好将日志删除掉,或者mShowLog设为flase。
  4. 还是推荐看完Blog之后,自己试着写一写。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注