关于Activity和Fragment的规范使用和系统回收activity的处理
2015-12-21 15:25
411 查看
一段存在问题的代码
onSaveInstanceState方法和savedInstanceState参数
onSaveInstanceState何时调用
onSaveInstanceState做了什么
onSaveInstanceState和savedInstanceState参数做了什么
再讨论之前代码为什么会有问题
总结
这篇文章主要分享两个话题:
Activity和Fragment之间如何安全调用
Activity的onSaveInstanceState方法
然后既然传入了MActivity的实例,那顺理成章实现了MFragment的实例回调其MActivity,一切看似都没什么问题,但是这段代码其实并不安全,当系统内存消耗太多,你会发现会有很多mContext的空指针异常。
造成上面异常主要是两个问题造成:Activity和Fragment之间调用不规范;忽略了系统回收后Activity的onCreate方法再次被调用的情形。简单的来说,你需要做到以下几点来规避这些问题:
Fragment的构造器最好不要自己实现,默认使用构造器。
如果想在创建Fragment的时候传递一些参数过去,可以使用
在使用add()、replace()方法时最好给Fragment加上tag,获取fragment实例通过
在Fragment中想要获取Activity实例使用
做到以上几点,就可以规避这类因为recreate造成的空指针异常了。具体原因,下面分析:
注意:如果你通过覆写
注意Activity和Fragment之间的规范使用。
onCreate并不只是创建Activity时调用,有可能会为了重现之前场景调用,因此你需要考虑在这种情况下是否会创建重复实例的问题。
onSaveInstanceState方法和savedInstanceState参数
onSaveInstanceState何时调用
onSaveInstanceState做了什么
onSaveInstanceState和savedInstanceState参数做了什么
再讨论之前代码为什么会有问题
总结
这篇文章主要分享两个话题:
Activity和Fragment之间如何安全调用
Activity的onSaveInstanceState方法
一段存在问题的代码
当你想写一个嵌套在Activity(假设为MActivity)中的Fragment(假设为MFragment),且MFragment在创建的时候可以从MActivity中获取一些参数从而帮助创建。于是你可能会在MFragment中写一个含参数的构造器,大概是这样:public ColorFragment(MActivity c, String param) { mContext = c; mParam = param; }
然后既然传入了MActivity的实例,那顺理成章实现了MFragment的实例回调其MActivity,一切看似都没什么问题,但是这段代码其实并不安全,当系统内存消耗太多,你会发现会有很多mContext的空指针异常。
造成上面异常主要是两个问题造成:Activity和Fragment之间调用不规范;忽略了系统回收后Activity的onCreate方法再次被调用的情形。简单的来说,你需要做到以下几点来规避这些问题:
Fragment的构造器最好不要自己实现,默认使用构造器。
如果想在创建Fragment的时候传递一些参数过去,可以使用
setArguments(Bundle args)方法,但是这个方法必须在创建了Fragment实例后马上使用。对应的Fragment可以通过
getArguments()获取参数
在使用add()、replace()方法时最好给Fragment加上tag,获取fragment实例通过
FragmentManager.findFragmentByTag(String tag)获取。
在Fragment中想要获取Activity实例使用
getActivity()方法。
做到以上几点,就可以规避这类因为recreate造成的空指针异常了。具体原因,下面分析:
onSaveInstanceState方法和savedInstanceState参数
今天之前,我常常忽略掉onSaveInstanceState方法和
onActivity(Bundle savedInstanceState)中传递的参数,其实很重要。主要起了一个系统回收Activity时”保存现场”的作用,让下一次打开时让用户觉得Activity从未被回收过。
onSaveInstanceState何时调用
APIguides说的是:before making the activity vulnerable to destruction, 自己实验,大概是用户打算将程序退到后台而不关闭调用(在执行了onPause()后,onStop()前)。个人感觉这里和API上说的在系统回收Activity前调用(具体什么时候没有说清楚),一般情况下都是退到后台就调用,大概是不同手机策略不一样吧
onSaveInstanceState做了什么
默认情况下,这个方法实现了存各种View的状态(注意各类View的实例是被回收了,只是他们的状态被保存了),其中Fragment的状态也被保存了。onSaveInstanceState和savedInstanceState参数做了什么
现在整体的描述下整个过程,第一次用户打开MActivity的时候,传入的savedInstanceState == null,然后当被退到后台时,
onSaveInstanceState方法执行,保存了一些View甚至fragment的状态,当内存过低,系统回收Activity以及Fragment的实例,当用户再次打开这个Activity时,执行
onCreate(Bundle savedInstanceState)且
savedInstanceState!=null,这里在MActivity的父类方法中根据
savedInstanceState对之前的现场进行了恢复(在
super.onCreate(savedInstanceState)中,具体代码可以点进去看看)。
注意:如果你通过覆写
onSaveInstanceState可以不让其执行父类的保存View的操作,但是第二次打开后
onCreate(Bundle savedInstanceState)中的
savedInstanceState依然不为空,只是
savedInstanceState.isEmpty()为
true。说明系统会判断这次打开是首次打开还是因为之前实例被回收了而打开。
再讨论之前代码为什么会有问题
结合以上,我们再来分析一下最开始的代码为什么会出错。在第一次启动时,在MActivity的onCreate中创建了MFragment,且传入了MActivity的实例(m1),MFragment使用的mContext也是这个实例的引用。用户将其退回到后台,Fragment的状态被保存下来。在一堆耗内存的操作后(使系统回收m1),第二次打开,MActivity再次创建(m2),在onCreate中通过super.onCreate(savedInstanceState)使得之前的fragment重新被创建,但是这里使用的是一个空构造器创建的fragment,而由于fragment中的mContext是对m1的引用,m1已经被系统回收,自然mContext为空,使用mContext会出现空指针异常。并且,由于在子类的onCreate()中又一次创建了fragment,所以此时你拥有两个fragment实例。
总结
希望通过这篇啰嗦的文章你可以意识到:注意Activity和Fragment之间的规范使用。
onCreate并不只是创建Activity时调用,有可能会为了重现之前场景调用,因此你需要考虑在这种情况下是否会创建重复实例的问题。
相关文章推荐
- IOS 标准时间与时间戳 相互转化
- 关于如何在启动时创建桌面快捷方式
- 用于linux与开发板之间下载文件的tftp配置详细总结
- Android 之版本更新
- JSON语法
- NSDate 与 NSDateFormatter
- 2014-2015 ACM-ICPC, NEERC, Moscow Subregional Contest F. Friends
- 微信相关 -- 可以通过调用下面的方法 请求对应的url 获取微信服务器返回的json数据
- c#删除转义字符的方法,删除\0后所有字符串(菜鸟级别)
- c3p0参数解释
- mysql prepare语句使用
- Please verify that your device’s clock is properly set, and that your signing certificate is not exp
- java 验证string是否为int或float型
- 数论欧拉函数
- Shell编程中的数组定义、遍历
- MySql查询条件区分大小写
- navicat for mysql 如何将表ID排序重1开始?
- 转: iPhone屏幕尺寸、分辨率及适配
- [Cryptography] KDC, CA, PKI, PGP
- javascript和Java 时间戳和PHP时间戳 的转换[10位和13位]