fragment重叠问题(add hide show方式)

来源:互联网 发布:响应式瀑布流 js插件 编辑:程序博客网 时间:2024/06/08 18:18

问题描述

想必各位Android开发者们,目前为止使用导航加fragment分模块的主界面的方式已经很常见了吧。

一般都是几个fragment在同一个activity里面,然后通过add hide show的方式去进行切换,这是最常见也是最实用的,因为这种方式不同于replace的切换方式,这种方式能保存fragment的状态,能带来更好的用户体验。

而使用add hide show这种方式来实现也是存在一些问题的,诸如这个我们要解决的问题。

—-fragment重叠问题

问题出现的场景就是:当你的APP按home键到后台之后,你再去打开别的APP进行一些很耗费内存的操作,比如,打开相机,打开系统图库等等,就会挤占很多的内存。这时候Android系统就会把你原来的APP给强制杀掉,然而它实际上并没有被完全杀死回收掉。如果你现在重新打开你的APP,就会出现fragment的重叠现象,就是你的几个fragment重叠在一起了。

解决办法

目前为止,我在百度上搜罗了很多资料,也看过很多的文献,大致发现了2种方法,然后我就跟我们群里的大神交流,一个一个地去亲测了那2种方法。先大致看一下,后面再详细描述。

先说,这里推荐大家使用第二种方法,简单明了。如果还想看看第二种方法以及详细推敲原理可以继续看下去。

—-方法一:

注释掉几个fragment所在的宿主activity里面的onSaveInstanceState方法的super.onSaveInstanceState,或者onRestoreInstanceState方法的super.onSaveInstanceState.

大致代码如下:

    @Override    protected void onSaveInstanceState(Bundle outState) {    //    super.onSaveInstanceState(outState);      }    或者    @Override    protected void onRestoreInstanceState(Bundle savedInstanceState) {    //    super.onRestoreInstanceState(savedInstanceState);     }

—-方法二:

利用activity里onCreate方法里面判断savedInstanceState是否为空,如果不为空,就用fragmentManager通过之前绑定的TAG找到相应的fragment,然后赋值,不再去重新add这几个fragment。

大致代码如下:

 if(savedInstanceState != null){            mChide= (ChideFragment)manager.findFragmentByTag(ChideFragment.class.getName());            mGuangde = (GuangdeFragment) manager.findFragmentByTag(GuangdeFragment.class.getName());            mWode = (WodeFragment) manager.findFragmentByTag(WodeFragment.class.getName());            mLiaode = (LiaodeFragment) manager.findFragmentByTag(LiaodeFragment.class.getName());    }else {            mChide = new ChideFragment();            mGuangde = new GuangdeFragment();            mWode = new WodeFragment();            mLiaode = new LiaodeFragment();   }

详细流程

好吧,现在我来详细地解释一下问题出现的原因。

我们的APP在后台因为内存不足或者放置了很久的时间,被系统销毁了,activity的视图view以及里面装的fragment也是被销毁了,但是实例是没有的。

当APP到后台之后,activity和fragment会执行onSaveInstanceState去保存数据,比如,里面各种子view的实例,当然也是包括fragment。

这里写图片描述

当我们重新打开APP的时候,activity会执行onRestoreInstanceState。

在问题没有出现的情况下,一切正常,onCreate里面的参数saveInstanceBundle是为空的,一切安基本流程走,就如同第一次打开APP的情况一样。

然而,如果是出现了我们现在所说的这样问题的话,saveInstanceBundle就不为空了。在onCreate的父方法执行的时候,就会去回滚一些数据,具体怎么实现的要去看activity的源码。

 @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);

所以,其实我们重新打开APP的时候,它已经存在了原来的4个fragment,在fragmentManager里面,然而又走了一遍自己的实例化和初始化流程,又重新创建了4个,如果看fragmentManager里面的话会有8个fragment。

创建了8个fragment的视图

然而前面4个被数据回滚回来的fragment在manager里面,但是之前的hide show 方法就失效了,所有的都是显示出来的,而且是按onCreateView的顺序,比如从导航栏第一到第四个按钮的顺序,就会出现一层一层的重叠。后面加进来的4个因为和他们一样,其实也看不出来了,反正就是重叠过来,重叠过去。

所以第一种办法就很简单,原理就是,阻止activity去保存或者回滚数据,这样前面的4个被数据回滚出来的fragment就不会存在或者不会创建。

就相当于重新走了一遍APP刚打开的流程,很多APP都是这样做的,如果从其他地方跳转回来,内存不足,它就会重新打开APP入口显示主界面。

第一种方法很简单,轻松就能解决,不过有点小风险就是,因为你注释掉了super的方法,以至于以后如果需要用到这个方法就有点麻烦了。


然而,第二种方法就有点麻烦了。

第二种方法是,从储存的数据中去回滚出来,然后重新使用,然而,这种方法的话是吃力不讨好的。

因为这4个回滚fragment实例的视图也是会被重新创建的,而且显示出来的时候也是4个一起,也会出现重叠现象,你要想不重叠,你需要自己再去hide ALL ,然后show你想要显示的那个。

相应的底部导航栏的checked状态也要对应,还有比如标题栏的图标或者其他什么一切都要跟你要想显示的那个对应。

我测试过了,其实这几个被保存下来的fragment实例,他们的状态是没有被保存的。也就是有点像新的,所以啊,第二种方法真的是吃力不讨好,太麻烦,不推荐使用。

最后

小小总结一下。
这次写这个博客也是因为感触有点深,因为为了解决这个问题还是花了很多心思,调试了很久的程序,为了达到内存的饱和,你要跳转到别的APP完成很多占内存的操作,比如,打开相机,打开图库,我都不晓得测试了多少次了,有点心累啊,啊哈哈。

不过在一次又一次的测试中,你就像在探索迷宫一样,每一次的尝试都在预示着你在成功之前遇到的岔路又少了一条。最后,终于探索出了自己的一片明亮的天地,过程中收获了很多,打印了很多log,跟着程序调试了很多很多遍。虽然结果很简单,但是探明结果的过程是很艰辛的,没事儿,继续加油!

希望能对大家有点帮助,我也算为这个圈子做出了一点点贡献,如有问题还希望大家提出来,我们一起探讨。

最后的最后要感谢一下 coder-pig大神的一起努力探讨,大家可以去光顾一下他的博客,写得很棒。

6 0
原创粉丝点击