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