Android 中使用Fragment所引发的

在自己开发的app中碰到了这样的问题,第一次进入有多个fragment的activity正常,第二次进入时,报错,其错误信息为:cannot perform this action after onsaveinstancestate

由此引发对fragment的探究:

一【译】 fragment transaction & activity state loss
   此段内容为对   http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html    一文有理解的翻译
   对于错误: cannot perform this action after onsaveinstancestate,此篇博客解释了这个错误发生的原因和时间,并且提出了保证app不发生此类错误的建议。

       为什么抛出这样的异常呢?
       在activity的状态被保存之后,用FragmentTransaction 进行 commit操作时很容易抛出这样的异常,常被称之为activity 状态丢失。要讲这个异常发生的始末,首先需要先探究一下onSaveInstanceState()这个函数,在安卓应用运行期间,安卓应用本身对自己的生命周期是有很少的控制权的,当内存吃紧的时候,安卓系统会将一些进程杀死,这样就让在后台运行的一些activity被消灭得悄无声息,为了让用户对此可以做出些防范措施,Framework就提供了onSaveInstanceState()这样的函数来保存activity在被异常杀死时的状态(onSaveInstance函数调用的时机会在下面继续讲解),这样做就保证了用户的体验状态,当再次调用这个在后台差点被杀死的Activity的时候,似乎一切都没有发生过。
        onSaveInstanceState()函数通过传递一个Bundle对象来存储Activity的状态,如activity dialog,fragment,views等的状态,其具体实现过程是系统将bundle对象打包通过binder接口传递给系统服务进程(具体哪个进程呢,好多系统服务进程呢。。不管了,反正是通过binder机制来传输的就对了),当系统决定再一次创建这个Activity的时候,就会将一个相同的bundler对象传给这个应用,使得应用可以快速地恢复这个activity的状态(可见系统在这里肯定还会对已经打开过的activity进行记录,说不准和bundle对象的对应就是一个map的结构,每次创建一个activity的时候都会去查询一下这个结构。。全部是猜测,有空了再去看源码)。
         那么异常是怎么发生的呢,onSaveInstanceState()只是对Activity在调用这个函数时的一个快照,FragmentTransaction 没有被记录下来是因为它本身并不作为Activity的状态中的一部分,从我们用户的角度来看,就是这个transaction遗失了,也就是UI状态的遗失,而Activity不惜一切代价防止状态的丢失,因此就抛出IllegalStateException  的异常。

       什么时候会抛出这样的异常
       在不同的SDK版本上,好像此异常的抛出状况会有所不同,特别是在引入了support支持包中的fragment之后,其实际上是因为在HoneyComb 3.0之后,android对onSaveInstanceState()这个函数的调用时机进行了修改,也就是意味着activity在何种状态下才能被系统悄无声息的杀死进行了修改,3.0之前onSaveInstanceState()在activity进入onpause()之前铁定被调用,3.0之后,onSaveInstanceState()函数在activity进入onstop()状态之前铁定被调用。
         
       怎样避免这样的异常
        1) 在Activity的生命周期里进行fragmentTransanction的提交操作时要小心,如果你是在onCreate()里面做这些操作,那应该是没有问题 (但实际我就是在onCreate里面出现的问题。。!不过我的问题不属于本博客讨论的状况,后续解释),但如果是在 onActivityResult() onStart() onResume()这样方法里,可能就会有问题了,比如说在onResume()中,对于fragmentActivity,在这个时刻开始对附着于它的fragment分发onResume(), 也就意味着之前的状态依然被保存着,从而不允许fragmenttransaction对状态进行改变,如果非要写在activity生命周期里的某个方法里面,可以写在 FragmentActivity#onResumeFragments()  or Activity#onPostResume()
,这些方法可以保证你的activity已经恢复到了一开始的状态。
stackoverflow上,对此问题 的一个解答,将提交操作写在onPostResume()里面
       2)在异步的回调方法里面,不要执行transaction。 如在AsynTask里面的onPostExcute() 和 LoaderManager.LoaderCallbacks#onLoadFinished() .主要是在这些方法里面你可能不知道activity的此时在什么样的生命周期里,比如说有以下事件:
    1. activity启动了AsynTask
         2. 用户这个时候按下了home键,导致activity调用了 onSaveInstanceState()和onstop()方法,
          3. AsynTask完成了异步任务,开始调用fragmenttransaction,而这个时候activity已经停止了
          4.  于是乎,异常发生了。
       这种情况下,Google 的安卓团队也似乎认为在异步任务执行完之后通过对FragmentTransaction对UI进行明显改变对用户来说并不是很好的体验,但是你的需求可能就是需要这样,commitAllowingStateLoss()在万不得已的情况下进行调用,或者是采用另外一种方法来进行解决,解决方案 http://stackoverflow.com/questions/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused   ( 博主太厉害,我还没有特别深入到此环境下进行细究,或许将来有需求会让我再次探究这种状况,已经让对按下home键时activity的生命周期和app的运作有了深深的考虑)
        3)使用 commitAllowingStateLoss() 作为最后的稻草,从函数名也可以看出来,这种提交是在运行状态丢失的情况下发生,那么其必然会导致一些有用信息的丢失,所以并不是很提倡这种方法的使用,当然在万不得已的情况,可以忽略状态丢失时,也可以调用此方法。

 二   onSaveInstanceState()和onRestoreInstanceState()
        既然说到了onSaveInstanceState,就不得不说Activity什么时候进行调用,就又得说一说它的另一个相似函数的调用时机,就又不得不说Activity在一些特殊状况下的生命周期,结合了一些博客总结如下:
         调用onSaveInstanceState的时机:
        1)按下home键时,或者长按home键,注意按下返回键是不会调用的,
        2)按下电源键,关闭了屏幕
        3)从当前Activity启动新的Activity的时候
        4)屏幕方向切换的时候
        onSaveInstanceState()的调用遵循一个重要原则,即当系统存在“未经你许可”时销毁了我们的activity的可能时,则onSaveInstanceState()会被系统调用。
          调用onRestoreInstanceState的时机:
 博客上是这么说的: onRestoreInstanceState()被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,感觉用到的比较少,还没有碰到有关于这个函数的一些问题。
          Activity的一些特殊情况下的生命周期,这篇文章讲解得比较好。
 
三   Activity has been destroyed
 
       我的问题依然没有解决,现在的bug,是这样子的了,搜了谷歌,看到了这样的解决方案 http://stackoverflow.com/questions/15207305/getting-the-error-java-lang-illegalstateexception-activity-has-been-destroyed  说是support library对fragment的bug, 需要对每个fragment进行detach,照做依然有问题,所以我觉得我要具体问题具体分析了,我的状况应该不属于常规状况,因为是自己做的app,是想将多种功能集合在一起的,所以是应用中的一个Activity启动了管理多个Fragment的 FragmentActivity,而不是第一个Activity就是FragmentActivity,我封装了fragmentManager,提供了一个controller类对fragment进行统一的管理,所以写了一单例模式,传入Activity进行获fragmentManager,那么问题就来了,当第二次再打开这个fragmentActivity的时候,上一个Activity所启动的单例fragmentController 还在,不仅造成了内存泄露不说,还导致这样的异常,突然就想到,单例模式会不会被系统回收这样的问题,  http://blog.csdn.net/zhengzhb/article/details/7331354  这篇文章还比较靠谱,看完之后得出的结论就是不会回收,顺便思维有飘散到 对JVM的管理上,静态变量存储在 方法区上。。。等等,拉回来,这样下去没完没了了,问题找到了,于是我变单例模式为非单例模式,果然问题解决了,不再造成bug了,考虑只是自己的练手app,需要界面上的呈现,所以就不再去管内存管理方面的东西了。
         果然具体问题需要具体分析,多想想自己代码是怎样写的,固然网上会有一般问题的解决方法,但是如果能够通过自己的分析找到问题的所在,那是非常有成就感的,当然有了问题还是需要去谷歌。。在你为成为大牛之前。


        
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值