怎样用好Fragment,避免掉坑(一)
2016-06-01 21:19
429 查看
本文是对Fragment一些坑的处理做一个记录,文章是参考YoKey 简书的Fragment全解析系列,想看更详细的可以去看,
我这里只是 简要总结一下,提炼精华。
1.内存重启
安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的
FragmentManager无论何时都会负责把Fragment保存起来
2.getActivity()为null
出现原因:这种情况一般出现在内存重启了,因为fragment的生命周期是和activity绑定的,执行了onDetach后fragment已经和activity断开联系。可能会造成这种异常,
解决方式:
在Fragment基类里设置一个Activity mActivity的全局变量,在
3.Fragment重叠异常
出现原因:如果你
解决方式:
(1)使用findFragmentByTag
即在
写法如下:
如果你想恢复到用户离开时的那个Fragment的界面,你还需要在
(2)用getSupportFragmentManager().getFragments()恢复
通过
从代码看起来,这种方式比较复杂,但是这种方式在一些场景下比第一种方式更加简便有效。
注意:有的朋友会在 onSaveInstanceState回调时去保存当前的fragment,重启时又把fragment拿出来,例如这样写
仅仅为了找回栈内的Fragment,使用
我这里只是 简要总结一下,提炼精华。
1.内存重启
安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的
onCreate方法调用后紧接着恢复(
从onAttach生命周期开始)。
FragmentManager无论何时都会负责把Fragment保存起来
2.getActivity()为null
出现原因:这种情况一般出现在内存重启了,因为fragment的生命周期是和activity绑定的,执行了onDetach后fragment已经和activity断开联系。可能会造成这种异常,
解决方式:
在Fragment基类里设置一个Activity mActivity的全局变量,在
onAttach(Activity activity)里赋值,使用mActivity代替
getActivity(),保证Fragment即使在
onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是相比空指针闪退,这种做法“安全”些),即:
protected Activity mActivity; @Override public void onAttach(Activity activity) { super.onAttach(activity); this.mActivity = activity; } /** * 如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替 */ @Override public void onAttach(Context context) { super.onAttach(context); this.mActivity = (Activity)context; }
3.Fragment重叠异常
出现原因:如果你
add()了几个Fragment,使用
show()、hide()方法控制,比如微信、QQ的底部tab等情景,如果你什么都不做的话,在“内存重启”后回到前台,app的这几个Fragment界面会重叠。原因是FragmentManager帮我们管理Fragment,每当我们离开该Activity,FragmentManager都会保存它的Fragments,当发生“内存重启”,他会从栈底向栈顶的顺序一次性恢复Fragments,并且全都都是以
show()的方式,所以我们看到了界面重叠。(如果是
replace,恢复形式和Activity一致,只有当你pop之后上一个Fragment才开始重新恢复,所有使用
replace不会造成重叠现象)
解决方式:
(1)使用findFragmentByTag
即在
add()或者
replace()时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过
findFragmentByTag找到对应的Fragment,并
hide()需要隐藏的fragment。
写法如下:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); TargetFragment targetFragment; HideFragment hideFragment; if (savedInstanceState != null) { // “内存重启”时调用 targetFragment = getSupportFragmentManager().findFragmentByTag(targetFragment.getClass().getName); hideFragment = getSupportFragmentManager().findFragmentByTag(hideFragment.getClass().getName); // 解决重叠问题 getFragmentManager().beginTransaction() .show(targetFragment) .hide(hideFragment) .commit(); }else{ // 正常时 targetFragment = TargetFragment.newInstance(); hideFragment = HideFragment.newInstance(); getFragmentManager().beginTransaction() .add(R.id.container, targetFragment, targetFragment.getClass().getName()) .add(R.id,container,hideFragment,hideFragment.getClass().getName()) .hide(hideFragment) .commit(); } }
如果你想恢复到用户离开时的那个Fragment的界面,你还需要在
onSaveInstanceState(Bundle outState)里保存离开时的那个可见的tag或下标,在
onCreate“内存重启”代码块中,取出tag/下标,进行恢复。
(2)用getSupportFragmentManager().getFragments()恢复
通过
getFragments()可以获取到当前FragmentManager管理的栈内所有Fragment。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); TargetFragment targetFragment; HideFragment hideFragment; if (savedInstanceState != null) { // “内存重启”时调用 List<Fragment> fragmentList = getSupportFragmentManager().getFragments(); for (Fragment fragment : fragmentList) { if(fragment instanceof TartgetFragment){ targetFragment = (TargetFragment)fragment; }else if(fragment instanceof HideFragment){ hideFragment = (HideFragment)fragment; } } // 解决重叠问题 getFragmentManager().beginTransaction() .show(targetFragment) .hide(hideFragment) .commit(); }else{ // 正常时 targetFragment = TargetFragment.newInstance(); hideFragment = HideFragment.newInstance(); // 这里add时,tag可传可不传 getFragmentManager().beginTransaction() .add(R.id.container) .add(R.id,container,hideFragment) .hide(hideFragment) .commit(); } }
从代码看起来,这种方式比较复杂,但是这种方式在一些场景下比第一种方式更加简便有效。
注意:有的朋友会在 onSaveInstanceState回调时去保存当前的fragment,重启时又把fragment拿出来,例如这样写
// 保存 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); getSupportFragmentManager().putFragment(outState, KEY, targetFragment); } // 恢复 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scrolling); if (savedInstanceState != null) { Fragment targetFragment = getSupportFragmentManager().getFragment(savedInstanceState, KEY); } }
仅仅为了找回栈内的Fragment,使用
putFragment(bundle, key, fragment)保存fragment,是完全没有必要的;因为FragmentManager在任何情况都会帮你存储Fragment,你要做的仅仅是在“内存重启”后,找回这些Fragment即可。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories