之前写过一个关于Fragment使用的帖子,但是并没有对生命周期做太详细的了解。因为那个时候主要是首页上的四个页签,然后用FrameLayout进行管理,使用那个显示哪个就可以了,然后在onCreateView里面初始化并返回我们要使用的View即可。

但是最近看到了一种写法就是一个Activity中放置多个数据显示格式相同,按内容划分的Fragment,而且确实按照正常无优化的方法,随着浏览时间的加长,数据量的曾加会先出现卡顿,然后内存溢出的问题。

那么就此场景举一个最常见的例子我的订单。(为了让viewpager有明显的过场,我每个Fragment设置的从红色开始的彩虹7色)

大家可以分析一下,订单的状态是不一样的,但是一般数据的显示格式是一样的。那么这个时候我们的设计思想就是首先订单是一个Activity,然后往里面放一个Viewpager,在写一个适配器,然后,将单个订单页封装成一个Fragment根据构造,然后请求不同的数据,那么就实现了,可是经过实践发现,没有想的那么简单。那么记录整个过程是非常有学习价值的所以我打算一步一步的拆解实现的过程,也是一个学习总结再升华的过程。

一、使用Fragment的小窍门,使用appV4下的Fragment而不是使用App包下的Fragment。为什么还是推荐V4下的Fragment?
1.当我们的使用第三方的时候会发现,第三方为了让自己的控件最大化的兼容(通常会将一套功能封装成一个Fragment),会选择V4下的Fragment,而我们如果使用app下的Fragment那么他FragmentManager是无法管理V4下的Fragment的,只有V4下的FragmentActivity所提供的supportFragmentManager才能管理V4下的Fragment所以为了让程序更好的兼容。
2.当我们使用Viewpager来管理Fragment的时候我们使用的FragmentPagerAdapter构造器中其FragmentManager是来自于AppV4包下面的,所以为了让我们自己的Fragment有更好的通用和适配性,这里推荐大家使用V4的Fragment为以后的扩充做好准备。

二、小窍门中我们说了使用Viewpager和Fragment相结合需要用到适配器。那么我们先来看下适配器,及带来的问题。
问题1:如果Fragment有网络加载,那么我们会发现除了当前的Fragment的网络启动了之外,别的Fragment的网络加载也启动了!
2:显示上没问题,左右滑动也都没事,但是越用越卡
3:如何利用Fragment的生命周期让我们的页面顺滑起来。
那么以上是我在这个开发过程中遇到的主要问题。
问题1:如果Fragment有网络加载,那么我们会发现除了当前的Fragment的网络启动了之外,别的Fragment的网络加载也启动了!
这里要说的是FragmentPagerAdapter的加载机制,为了让客户有更好的体验,那么当我们用ViewPager显示一个Fragment的时候,FragmentPagerAdapter会自动加载其两侧的Fragment,进而让客户滑动的时候感觉没有明显的卡顿和数据加载,但是如果我们写的网络加载里面有Toast的话,那么就会发生一些乌龙时间,好比第一页是有数据的,这个时候数据已经正常显示了,但是我在无数据的时候给了一个无数据提示,而第二页恰好没数据,那么乌龙就发生了,在有数据的页接到了无数据提示。而且更主要的是,如果我们的订单约定是是显示10条,那么同时开启两个数据加载链接必然产生两个请求数据返回的时间加长!这样对应当前想使用的页体验又是不好的。在一个我们缓存两页,如果图片过多,又没有写好数据回收的工作,那么刚开始就是卡顿,再然后就是内存溢出。

问题2:就是左右加载的内存没有被释放导致的,这里建议使用Fresco去加载图片,因为Fresco的内存处理机制实在是太棒了!嗯这个问题点也主要是想说这个,当我使用Fresco的缓存策略的时候,只要图片不在当前页面的显示那么图片的内存就被回收了。这个问题是之前使用Glide导致的,因为内存回收的不及时,最后导致滑动了几次之后就崩溃了。

问题3:这里就是要说一下当我们滑动的时候,我们需要注意一下在FragmentPagerAdapter下的Fragment的生命周期。
当我们从Activity进入一个Fragment的时候,其流程是
显示出来的页:onAttach->onCreate->onCreateView->onActivityCreated ->onStart ->onResume
之后两边的页:onAttach->onCreate->onCreateView->onActivityCreated ->onStart ->onResume
打印Log的时候我们可以看见同样的生命周期会输出三遍这就是说当前的Fragment和左右的Fragment都被加载了。所以当我们将网络加载的代码放到onCreateView里面的时候,当我们开启一个Fragment那么其实同时开启了3个网络加载线程!所以这是我们不希望的。这就可能导致之前说的乌龙事件。这里给出的解决策略是在onsetUserVisibleHint()方法中进行数据加载,当我们的Fragment显示的时候,启动数据加载单元,那么我们的Fragment其实只是初始化了一些固定View并不会占用太多的内存。

那么我们继续往下走,当我们滑动Fragment的时候会看见那些生命周期那?
被隐藏的页面:onPause->onStop->onDestroyView
新建的页面:onAttach->onCreate->onCreateView->onActivityCreated ->onStart ->onResume
这里有一个重点,就是Fragment的生命周期到onDestroyView就结束了,而并是onDestory,这是很重要的,这就说明只有我们和Fragment.RootView相关的View被销毁了,而其他的数据还在。就那这个列表页来说,我需要写下来刷新和上拉加载,那么我就需要一个全局的Adapter那么在Fragment#onDestroyView的时候我的数据就还在,那么我就可以结合之前说的onsetUserVisibleHint()中根据adapter是否被初始化来决定是否再次进行网络加载!进而起到减少网络加载,和在使用时加载的需求。

这里继续为大家补充一些关于setUserVisibleHint的知识:
setUserVisibleHint()在Fragment创建时会先被调用一次,传入isVisibleToUser = false
如果当前Fragment可见,那么setUserVisibleHint()会再次被调用一次,传入isVisibleToUser = true
如果Fragment从可见->不可见,那么setUserVisibleHint()也会被调用,传入isVisibleToUser = false

总结:setUserVisibleHint()除了Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调
如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,那么就需要重新封装一下
这里贴出来封装好的Fragment

public abstract class LazyFragment extends Fragment {private static final String TAG = LazyFragment.class.getSimpleName();private boolean isFragmentVisible;private boolean isReuseView;private boolean isFirstVisible;private View rootView;//setUserVisibleHint()在Fragment创建时会先被调用一次,传入isVisibleToUser = false//如果当前Fragment可见,那么setUserVisibleHint()会再次被调用一次,传入isVisibleToUser = true//如果Fragment从可见->不可见,那么setUserVisibleHint()也会被调用,传入isVisibleToUser = false//总结:setUserVisibleHint()除了Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调//如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,那么就需要重新封装一个@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);//setUserVisibleHint()有可能在fragment的生命周期外被调用if (rootView == null) {return;}if (isFirstVisible && isVisibleToUser) {onFragmentFirstVisible();isFirstVisible = false;}if (isVisibleToUser) {onFragmentVisibleChange(true);isFragmentVisible = true;return;}if (isFragmentVisible) {isFragmentVisible = false;onFragmentVisibleChange(false);}}@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);initVariable();}@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {//如果setUserVisibleHint()在rootView创建前调用时,那么//就等到rootView创建完后才回调onFragmentVisibleChange(true)//保证onFragmentVisibleChange()的回调发生在rootView创建完成之后,以便支持ui操作if (rootView == null) {rootView = view;if (getUserVisibleHint()) {if (isFirstVisible) {onFragmentFirstVisible();isFirstVisible = false;}onFragmentVisibleChange(true);isFragmentVisible = true;}}super.onViewCreated(isReuseView ? rootView : view, savedInstanceState);}@Overridepublic void onDestroyView() {super.onDestroyView();}@Overridepublic void onDestroy() {super.onDestroy();initVariable();}private void initVariable() {isFirstVisible = true;isFragmentVisible = false;rootView = null;isReuseView = true;}/*** 设置是否使用 view 的复用,默认开启* view 的复用是指,ViewPager 在销毁和重建 Fragment 时会不断调用 onCreateView() -> onDestroyView()* 之间的生命函数,这样可能会出现重复创建 view 的情况,导致界面上显示多个相同的 Fragment* view 的复用其实就是指保存第一次创建的 view,后面再 onCreateView() 时直接返回第一次创建的 view** @param isReuse*/protected void reuseView(boolean isReuse) {isReuseView = isReuse;}/*** 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调* 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题** 可在该回调方法里进行一些ui显示与隐藏,比如加载框的显示和隐藏** @param isVisible true  不可见 -> 可见*                  false 可见  -> 不可见*/protected void onFragmentVisibleChange(boolean isVisible) {}/*** 在fragment首次可见时回调,可在这里进行加载数据,保证只在第一次打开Fragment时才会加载数据,* 这样就可以防止每次进入都重复加载数据* 该方法会在 onFragmentVisibleChange() 之前调用,所以第一次打开时,可以用一个全局变量表示数据下载状态,* 然后在该方法内将状态设置为下载状态,接着去执行下载的任务* 最后在 onFragmentVisibleChange() 里根据数据下载状态来控制下载进度ui控件的显示与隐藏*/protected void onFragmentFirstVisible() {}protected boolean isFragmentVisible() {return isFragmentVisible;}}

FragmentStatePagerAdapter和FragmentPagerAdapter的比较
1.FragmentPagerAdapter适用于Fragment比较少的情况,因为我们会把每一个Fragment保存在内存中,不用每次切换的时候,去保存现场,切换回来在重新创建,所以用户体验比较好。没有onDestored()!
2.而对于我们的Fragment比较多的情况,我们需要切换的时候销毁以前的Fragment以释放内存,就可以使用FragmentStatePagerAdapter。onDestored()!

从FragmentPagerAdapter看Fragment 生命周期相关推荐

  1. android viewpager fragment 生命周期,ViewPager中Fragment的生命周期

    网上有很多Fragment生命周期的帖子,但是看了一下,没有找到自己想了解的东西,于是决定自己动手体验一下这个生命周期.主要想了解以下几个问题: Activity中的Fragment的生命周期 Vie ...

  2. Fragment生命周期及其使用

    Fragment是随着Android 3.0推出时携带的一部分,若是在1.6版本中使用,必须引入相应的支持包.Fragment最大的优点是你可以根据不同设备的屏幕大小创建动态的UI.Fragment有 ...

  3. 项目疑难杂症记录(五):fragment生命周期都回调了,却不见其页面展示

    继续记录我的疑难bug解决过程,这次要说的bug相比前几篇来说,更难定位,因为影响较大,直接导致不解决这个bug,根本就没有办法出版本,两三个同事定位了半天也没有结果,最后我自告奋勇的暂时放下手中的工 ...

  4. activity和fragment生命周期

    activity和fragment生命周期看这两张图片就可以清晰的知道四大组件之一的activity和碎片的运作流程:如果想亲自看看它在代码中是运作流程就可以打Log看下,由于这样代码量有点多,而且简 ...

  5. Android中Fragment生命周期和基本用法

    1.基本概念 1. Fragment是什么? Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fra ...

  6. android屏幕旋转生命周期,Activity、Fragment生命周期---横竖屏切换的生命周期

    先贴出一张大家众所周知activity流程图 onCreate():创建Activity调用,用于Activity的初始化,还有个Bundle类型的参数,可以访问以前存储的状态.onStart():A ...

  7. Android:Fragment生命周期(结合Activity的生命周期进行分析)

    文章目录 前言 一.Fragment生命周期概述 启动 退出 二.Fragment生命周期细述 1.onCreate(Bundle) 2.onCreateView(LayoutInflater, Vi ...

  8. Lifecycle Activity和Fragment生命周期感知组件 LifecycleObserver MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  9. Fragment生命周期详解

    关于Fragment的生命周期,博主写过Activity与Fragment生命周期详解,基本上把Fragment的生命周期详细介绍过,但是那仅仅是创建一个Fragmnet时的生命周期,而事实上Frag ...

最新文章

  1. 最近火爆全网!这个 Python 项目很骚气!
  2. MySQL 优化实战记录
  3. 这套1600赞的NLP课程已开放,面向实战,视频代码都有丨资源
  4. 多个容器一起打包_程序员修神之路容器技术为什么会这么流行(记得去抽奖)
  5. android studio中把c/c++文件编译成.so库(一)
  6. html超链接同一页面,你绝对想要的HTML页面超链接的修改问题
  7. 计算机分屏解决方案,整套解决方案:使窗口布局更高效,为Windows平台编译分屏工具...
  8. python stm32f401_使用Python编程STM32F401 Nucleo开发板快速入门
  9. Python读取相对路径文件
  10. Flutter加载大图内存问题处理
  11. Kettle闪退,以及Kettle资源库connect报错小解
  12. 【数据库】HIVE SQL实现本月一号、月底的提取
  13. 第一章 Hadoop启动Shell启动脚本分析--基于hadoop-0.20.2-cdh3u1
  14. 【Arduino】重生之Arduino 学僧(1)----Arduino简介
  15. python random seed原理_numpy.random.seed()的使用实例解析
  16. MySQL 数据表查询
  17. 设计一个亿级高并发系统架构 - 12306火车票核心场景DDD领域建模
  18. 律师教你借条正规写法,一定要收藏
  19. 配置本地yum源和阿里云源
  20. 试验设计与数据处理_【工作动态】规划院组织开展国内首次AisaIBIS超光谱仪机载系统挂飞综合试验...

热门文章

  1. 信号数据shannon entropy计算
  2. Cocos2d-x 游戏中子弹的设计
  3. 微型计算机MMX的技术特点,自考04732微型计算机及接口技术试卷(答案全面)
  4. Android:adb卸载系统应用软技能
  5. 晓_【斗战神学习二十四】一手交钱,一手交货
  6. Antd pro中ProFormSelect使用initialValues
  7. 北京大学软件与微电子学院学习经验文章集78篇和1个专题
  8. 不怕得罪人地推荐这9本黑客书籍
  9. 李宏毅线性代数笔记7 子空间
  10. [vuex] unknown action type: jia1