Fragment进阶使用技巧
内存重启
当App运行在后台,系统由于资源紧张把App杀死回收,此时App从后台切回前台会产生重启现象,我们把这种情况称为“内存重启”。另外,屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似。
在系统要把app回收之前,系统会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的onCreate方法调用后紧接着恢复(从onAttach生命周期开始)。
Fragment常用方法
Fragment 的动态添加、删除等操作都需要借助于FragmentTransaction类来完成,下面是几种常用的方法:
- add() 系列:添加 Fragment 到 Activity 界面中;
- remove():移除 Activity 中的指定 Fragment;
- replace() 系列:通过内部调用 remove() 和 add() 完成 Fragment 的修改;
- hide() 和 show():隐藏和显示 Activity 中的 Fragment;
- addToBackStack():添加当前事务到回退栈中,即当按下返回键时,界面回归到当前事物状态;
- commit():提交事务,所有通过上述方法对 Fragment 的改动都必须通过调用 commit() 方法完成提交
add、show、hide、replace的区别
区别:
- show(),hide()最终是让Fragment的View setVisibility(true还是false),不会调用生命周期;
- replace()的话会销毁视图,即调用onDestoryView、onCreateView等一系列生命周期;
- 注意:add()和 replace()不要在同一个阶级的FragmentManager里混搭使用。
使用场景:
- 如果你有一个很高的概率会再次使用当前的Fragment,建议使用show(),hide(),可以提高性能。
- 大部分情况下用show(),hide(),而不是replace
- 注意:如果你的app有大量图片,这时更好的方式可能是replace,配合你的图片框架在Fragment视图销毁时,回收其图片所占的内存。
onHiddenChanged()回调时机
当使用add()+show()/hide()跳转新的Fragment时,旧的Fragment回调onHiddenChanged(),不会回调onStop()等生命周期方法,而新的Fragment在创建时是不会回调onHiddenChanged()的。
1 |
|
为何不使用构造传值
当Activity重建时会调用Fragment的无参构造来重建Fragment,会导致数据丢失。
Fragment栈视图
每个Fragment以及宿主Activity(继承自FragmentActivity)都会在创建时,初始化一个FragmentManager对象,处理好Fragment嵌套问题的关键,就是理清这些不同阶级的栈视图。
获取FragmentManager对象
- 对于宿主Activity,getSupportFragmentManager()获取的FragmentActivity的FragmentManager对象;
- 对于Fragment,getFragmentManager()是获取的是父Fragment(如果没有,则是FragmentActivity)的FragmentManager对象,而getChildFragmentManager()是获取自己的FragmentManager对象。
Fragment之懒加载
懒加载,其实也就是延迟加载,就是等到该页面的UI展示给用户时,再加载该页面的数据(从网络、数据库等)。
Fragment中实现懒加载主要涉及setUserVisibleHint()方法,该方法会在onCreateView之前执行,当Fragment从可见到不可见或是从不可见到可见时,都会调用此方法。此外可以使用getUserVisibleHint()来判断Fragment是否可见。
ViewPager与Fragment
在使用viewpager(或其他容器)与多个Fragment来组合使用,ViewPager 会默认一次加载当前页面前后隔一个页面,即使设置setofflimit(0)也无效果,也会预加载。这样把我们看不到的页面的数据也加载了,大大降低了性能,浪费初始化资源。然而我们就采用懒加载技术,只让用户看到的页面才会加载他的数据,大大提高效率。
代码案例:
1 |
|
在BaseMVPLazyFragment中需要在onActivityCreated()及setUserVisibleHint()方法中都调了一次lazyLoad() 方法。如果仅仅在setUserVisibleHint()调用lazyLoad(),当默认首页首先加载时会导致viewPager的首页第一次展示时没有数据显示,切换一下才会有数据。因为首页fragment的setUserVisible()在onActivityCreated() 之前调用,此时isPrepared为false 导致首页fragment 没能调用onLazyLoad()方法加载数据。
onLazyLoad()加载数据条件
- getUserVisibleHint()会返回是否可见状态,这是fragment实现懒加载的关键,只有fragment 可见才会调用onLazyLoad() 加载数据。
- isPrepared参数在系统调用onActivityCreated时设置为true,这时onCreateView方法已调用完毕(一般我们在这方法里执行findviewbyid等方法),确保 onLazyLoad()方法不会报空指针异常。
- isLazyLoaded确保ViewPager来回切换时BaseFragment的initData方法不会被重复调用,onLazyLoad在该Fragment的整个生命周期只调用一次,第一次调用onLazyLoad()方法后马上执行 isLazyLoaded = true。
- 然后再继承这个BaseMVPLazyFragment实现onLazyLoad() 方法就行。他会自动控制当fragment 展现出来时,才会加载数据
使用FragmentPagerAdapter需要注意的
在给ViewPager绑定FragmentPagerAdapter时,new FragmentPagerAdapter(fragmentManager)的FragmentManager,一定要保证正确。如果ViewPager是Activity内的控件,则传递getSupportFragmentManager(),如果是Fragment内的控件,则传递getChildFragmentManager()。只要记住ViewPager内的Fragments是当前组件的子Fragment这个原则即可。
关于事务Transaction
Fragment不能独立存在,它必须嵌入到activity中,而且Fragment的生命周期直接受所在的activity的影响。
transaction只是记录了从一个状态到另一个状态的变化过程,即比如从FragmentA替换到FragmentB的过程,当通过函数transaction.addToBackStack(null)将这个事务添加到回退栈,则会记录这个事务的状态变化过程,如从FragmentA —>FragmentB,当用户点击手机回退键时,因为transaction的状态变化过程被保存,则可以将事务的状态变化过程还原,即将FragmentB —> FragmentA。
1 |
|
关于commit()与commitAllowingStateLoss()
若在Activity的onSaveInstanceState()方法之后调用commit()方法提交事务会出错,并提示“Can not perform this action after onSaveInstanceState”。因为onSaveInstanceState方法是在该Activity即将被销毁前调用,来保存Activity数据的,如果在保存完状态后再给它添加Fragment就会出错。如果无法确定onSaveInstanceState()的调用时机,使用commitAllowingStateLoss()方法。
看看源码:
1 |
|
从源代码来看,它们调用了同一个方法只是传递的参数不同,那么就是这个参数导致的不同了,继续往下看:
1 |
|
可以看到allowStateLoss的值主要是影响了checkStateLoss()的调用,当allowStateLoss为true时就会在onSaveInstanceState()被调用后抛出异常。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!