时间:2021-05-19
Fragment的主要意义就是提供与Activity绑定的生命周期回调。
Fragment不一定要向Activity的视图层级中添加View. 当某个模块需要获得Activity的生命周期回调的时候,就可以考虑通过Fragment来实现.
例如: DialogFragment, 调用show方法来显示一个Dialog(这个一个子Window,并不在Activity的视图层级中),当旋屏时,DialogFragment利用onDestroyView回调来dismiss Dialog,然后Activity重建之后,DialogFragment利用onStart回调再显示Dialog。
当然,我们也可以创建一个完全没有UI的Fragment,比如BackgroundWorkerFragment,在onResume的时候执行一个Task,在onPause的时候暂停一个Task。
Fragment 生命周期
先来回顾一下基础知识,Fragment的生命周期图如下:
说明:总的来说,Fragment和Activity的生命周期类似。需要注意的是,它相比于Activity,多了onAttach(), onDetch(), onCreateView()和onDestroyView()这几个回调函数;但是,却少了onRestart()。
Fragment的生命周期非常复杂,分为以下几种情况:
FragmentTransaction
Can not perform this action after onSaveInstanceState
在使用Fragment的过程中,常常会遇到在Activity的onSaveInstanceState方法调用之后,操作commit或者popBackStack而导致的crash.
因为在onSaveInstanceState方法之后的操作状态可能会丢失,因此Android framework默认会抛出一个异常.
对于commit方法来说,单纯避免这个异常很简单,使用commitAllowingStateLoss方法即可.但是popBackStack以及popBackStackImmediate也都会检查state(checkStateLoss),特别需要注意的是Activity的onBackPressed方法
如果onBackPressed在onSavedInstanceState之后调用,那么就会crash.
onBackPressed的调用时机:
* targetSdkVersion <= 5,在onKeyDown中调用
* targetSdkVersion > 5,在onKeyUp中调用
onSavedInstanceState的调用时机(如果调用的话):
* 一定在onStop之前
* 可能在onPause之前,也可能在onPause与onStop之间
需要注意的是: onSavedInstanceState方法不一定会调用,只有在Activity因为某些原因而被Framework销毁,并且之后还需要重新创建的情况,才需要调用(例如:旋屏,或者内存不足而回收返回栈中的某些Activity)
举例:
* Activity A在前台时,屏幕逐渐变暗直至锁屏,那么A的onSavedInstanceState会被调用
* Activity A start Activity B,Activity A的onSavedInstanceState会被调用
* Activity A因为返回键或者finish调用而返回到上一个界面,那么A的onSavedInstanceState不会被调用
因此,当onBackPressed在onSavedInstanceState方法之后调用,就一定会crash.解决方法主要有两种:
重写Activity的onSavedInstanceState()方法,并且注释掉super调用.
这种方法能避免crash,但是它会导致整个Activity的状态丢失.以DialogFragment为例,正常情况下,显示的DialogFragment在旋屏Activity重新创建之后,不需要我们处理,Dialog会自动显示出来(参见DialogFragment.onStart()),但是注释掉Activity的onSavedInstanceState()方法之后,Fragment状态丢失,Activity重新创建之后,Dialog也就不会再显示出来了.
更好且通用的做法:在调用commit,popBackStack以及onBackPressed方法之前,判断onSavedInstanceState()方法是否已经执行,并且onResume方法还没有执行,如果不是,那么直接操作,否则加入到pending队列,等待onResumeFragments或者onPostResume之后再执行.
注意:不要在onResume中操作,因为这时候FragmentManager中的mStateSaved依然可能是true.(如果执行顺序是onSavedInstanceState()->onPause()->onResume() 或者 onPause()->onSavedInstanceState()->onResume())
例如:
startActivityForResult
requestCode的可用区间:
1.Activity: [Integer.MIN_VALUE, Integer.MAX_VALUE]
(1)当requestCode取值在[Integer.MIN_VALUE, -1]区间中,效果和startActivity()一样,不会收到onActivityResult()回调
(2)内置的Fragment可用requestCode的区间和Activity相同
2.support库: Fragment,以及FragmentActivity:[-1, 65535]
(1)requestCode == -1,效果和startActivity()一样,不会收到onActivityResult()回调
(2)requestCode 在 [Integer.MIN_VALUE, -2]或者[65536, Integer.MAX_VALUE]之间,会抛出异常(requestCode只能使用低16比特)
建议: requestCode的取值统一限制在[-1, 65535]之间
嵌套Fragment
首先要说的是尽量不要使用嵌套Fragment.
当在嵌套Fragment中使用startActivityForResult()时,会遇到的问题:
所有的Fragment都收不到onActivityResult()
某个level 1 的Fragment收到了onActivityResult()
总之那个发起startActivityForResult()的嵌套Fragment是一定不会收到onActivityResult()回调的.
原因如下:(可参考上面说的requestCode)
FragmentActivity.startActivityFromFragment()会改动requestCode,用高16比特存储Fragment在FragmentManager中的index,而低16比特作为Fragment可用的requestCode.在FragmentActivity.onActivityResult()中,根据高16比特,从FragmentManager中找到对应的Fragment,然后将低16比特的值作为requestCode,调用Fragment.onActivityResult().
那么requestCode中只能存储一个index,即root FragmentManager中的Fragment index.因此就会出现上面所列出的情形:
解决方案:
Tips
开发的时候,可以打开Fragment相关的调试信息
如果需要在Fragment的onResume都执行完后再执行某个操作,可以重写onPostResume()方法,一定要调用 super.onPostResume()
1.IllegalStateException(Fragment not attached to Activity)的问题
这个异常通常的发生情况是:在Fragment中启动一个异步任务,然后在回调中执行和resource相关的操作(getString(...)),或者startActivity(...)之类的操作.但是这个时候Fragment可能已经被detach了,所以它的mHost==null,因此在执行这些操作之前,需要先判断一下isAdded().
注意: 这里不要使用isDetached()来判断,因为Fragment被detach之后,它的isDetached()方法依然可能返回false
2.如果Fragment A是因为被replace而detach的,那么它的isDetached()将返回false
3.如果Fragment A对应的FragmentTransaction被加入到返回栈中,因为出栈而detach,那么它的isDetached()将返回true
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
基础的定义及使用方法网上有很多不再概述,这儿主要写本人所使用项目时碰到的一些问题及解决方案注:本人使用此插件主要用来上传图片之用,插件其他上传文件没有涉及一、上
关于本文本文主要总结网站编写以来在传递JSON数据方面遇到的一些问题以及目前采用的解决方案。网站数据库采用MongoDB,后端是Python,前端采用“半分离”
最近做android项目遇到这个问题,为了保持app风格一致,需要将原生的EditText底线颜色改成橙色。网上搜了一些解决方案,特此记录总结一下。效果图默认的
WindowsServer2008安装时如果出现出现一些问题,下面给你提供一些解决方案。 1.在测试计算机上安装WindowsServer2008。 2.在
如果各位想要成为一名优秀的京东店铺运营者,首先就要学会去分析店铺的现实情况,然后得出店铺的一些问题,并且提出解决方案,然后再根据解决方案去实施优化,到底应该怎么