您的位置:首页 > 其它

关于Activity和Fragment的规范使用和系统回收activity的处理

2015-12-21 15:25 411 查看
一段存在问题的代码

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时调用,有可能会为了重现之前场景调用,因此你需要考虑在这种情况下是否会创建重复实例的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: