可能很多朋友在使用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方法中包含如下代码:
在TestFragment的onCreate方法中增加一条打印日志,然后不断旋转屏幕,你会发现,每旋转一次屏幕TestFragment的打印日志都会比上次多打出一条。
这是因为,这个fragment的管理栈在Activity因为屏幕旋转被销毁时而由系统进行了保存,没旋转一次意味着Fragment栈中就多增加一个fragment,而这些fragment都会因为Activity的重建而自己本身也重新创建一次。
Tips:setRetainInstance方法可以防止Fragment重建
详细做法见:《Activity重建时保持Fragment状态的方法》