记一个State Loss断言

来源:互联网 发布:度娘软件下载 编辑:程序博客网 时间:2024/06/05 19:45

可能很多朋友在使用v4兼容包中的Fragment方法进行应用开发时都遇到过这种异常,诈一看调用栈,根本无从下手解决。下面我就详细分析下这个断言出现的原因和解决方法。

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341) at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352) at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595) at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

原因

在Android的Honeycomb(3.0)版本开始,Activity只有在被stop后才可能会被杀死。而在Honeycomb版本之前,Activity在被pause后就有可能被杀死。系统在Activity被杀死之前会调用onSaveInstanceState()方法来保存当前Activity状态,也即相当于给当前Activity做一个快照,为了后续重新创建该Activity时可以恢复到被杀死前状态,保证用户体验。这就是说onSaveInstanceState方法在Honeycomb版本之前会在onPause之前调用,Honeycomb之后会在onStop之前调用。如下表所示:
这里写图片描述

在Honeycomb版本中Android引入的一个最大特性就是Fragment机制。而Fragment的管理需要用Activity中的FragmentManager通过事务提交的方式来变更当前Activity中Fragment的状态。如果程序在系统调用onSaveInstanceState之后进行了commit操作。那么这些状态就会被丢失,就会抛出异常。

v4兼容包,因为需要兼容3.0以前版本,如果commit在onPause后就抛出异常则对3.0以后版本来说就显得过于严格,所以兼容包对不同版本区别对待。具体表现如下表:
这里写图片描述
在3.0版本以前,使用v4的commit时,如果发生在onPause和onStop之间则不抛出异常,而直接做状态丢失处理。

举例

一个Activity的onCreate方法中包含如下代码:
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new TestFragment()).commit();

在TestFragment的onCreate方法中增加一条打印日志,然后不断旋转屏幕,你会发现,每旋转一次屏幕TestFragment的打印日志都会比上次多打出一条。

这是因为,这个fragment的管理栈在Activity因为屏幕旋转被销毁时而由系统进行了保存,没旋转一次意味着Fragment栈中就多增加一个fragment,而这些fragment都会因为Activity的重建而自己本身也重新创建一次。

Tips:setRetainInstance方法可以防止Fragment重建
详细做法见:《Activity重建时保持Fragment状态的方法》

注意事项

注意事务提交时所处的Activity生命周期

避免在异步方法中使用事务

使用commitAllowingStateLoss

1 0
原创粉丝点击