java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager:1377) at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager:504) at android.support.v4.app.FragmentActivity.onBackPressed(FragmentActivity:178) at android.app.Activity.onKeyUp(Activity.java:2207) at android.view.KeyEvent.dispatch(KeyEvent.java:2664) at android.app.Activity.dispatchKeyEvent(Activity.java:2437) atcom.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1975) at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4013) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3987) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3553) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3603) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3572) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3679) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3580) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3736) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3553) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3603) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3572) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3580) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3553) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3603) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3572) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3712) at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:3877) at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2027) at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1721) at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1712) at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2004) at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:138) at android.os.Looper.loop(Looper.java:123) at android.app.ActivityThread.main(ActivityThread.java:5028) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) atcom.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788) atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:604) at dalvik.system.NativeStart.main(Native Method)
概括的讲,onSaveInstanceState 这个方法会在activity
将要被kill之前被调用以保存每个实例的状态,以保证在将来的某个时刻回来时可以恢复到原来的状态,但和activity 的生命周期方法onStop
和 onPause 不一样,与两者并没有绝对的先后调用顺序,或者说并非所有场景都会调用onSaveInstanceState
方法。那么onSaveInstanceState 方法何时会被调用呢,或者这么问,什么时候activity 会被系统kill
掉呢?有以下几种比较常见的场景
(1)用户主动按下home 键,系统不能确认activity 是否会被销毁,实际上此刻系统也无法预测将来的场景,比如说内存占用,应用运行情况等,所以系统会调用onSaveInstanceState保存activity状态 ;
(2)activity位于前台,按下电源键,直接锁屏;
(3)横竖屏切换;
(4)activity B启动后位于activity A之前,在某个时刻activity A因为系统回收资源的问题要被kill掉,A通过onSaveInstanceState保存状态。
(5)重置应用偏好
我们的activity在某种场景下处于被kill 掉的边缘,系统就调用了onSaveInstanceState 方法,这个方法里面会调用
FragmentManager saveAllState 方法,将fragment 的状态保存,在状态保存后用户又主动调了
onBackPressed ,而这个方法的超类super.onBackPressed 方法会判断FragmentManager
是否保存了状态,如果已经保存就会抛出IllegalStateException 的异常 。
此外,还有一个比较类似的异常堆栈信息:
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)
这一类异常原因是在activity 调用onSaveInstanceState后,调用了fragmenttransaction 的commit方法所致,如果说onBackPressed 的异常是出现在用户按back后,那么在何处调用commit会导致IllegalStateException异常呢?实际上在api 11(Honeycomb)之前,如果onSaveInstanceState 方法被调用,那么肯定是在onPause 生命周期方法前,但api11以后,却只能保证在onStop 生命周期方法前,和onPause 方法并没有明确的先后调用顺序,正是由于此处生命周期的微小变化,导致api11后,如果在onPause 和 onStop 之间调用commit ,将有可能抛出一个IllegalStateException异常告知状态丢失。
1、关于commit 方法的调用异常处理方法
(1)在activity生命周期函数内谨慎使用commit 方法 ,一般情况下如果能在onCreate 中调用,基本不会出现问题,但是如果在onResume onStart 等方法中调用就需要格外注意,比如说FragmetActivity 的onResume 方法 ,在某些场景下onResume 方法被调用之前,可能依然保存着之前的状态导致异常 。
Dispatch onResume()tofragments. Notethatforbetter inter-operationwitholder versionsoftheplatform,atthepointofthis callthefragments attachedtotheactivity arenotresumed. This meansthatinsomecasestheprevious state may still be saved,notallowing fragment transactionsthatmodifythestate.
(2)尽可能避免在一些和生命周期函数异步的方法中调用commit,如AsyncTask 等。
(3)实在没法确定调用时机时,可以用commitAllowingStateLoss 代替 commit ,commitAllowingStateLoss 在状态丢失时不会抛出任何异常,但也正因为如此在一些必须确保状态被保存的场合,最好不要使用 commitAllowingStateLoss 方法。
eg:
if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
DummyFragment dummyFragment = DummyFragment.newInstance();
ft.add(R.id.dummy_fragment_layout, dummyFragment);
ft.commitAllowingStateLoss();
}
2、针对onbackpress 导致的异常,也有几种解决手段
(1)在api11 以上 不调用super 的onSaveInstanceState 方法
protected void onSaveInstanceState(Bundle outState) {//No call for super(). Bug on API Level > 11.}
这样做的后果会导致activity 和 fragment 的所有状态,在activity 被系统杀死掉后无法保存,所以如果有保存状态的需要,这个方法是不适用的。
(2)重写 onBackPressed 方法,不调用super.onBackPressed ,直接调用finish。原因很简单,super.onBackPressed 里面会调用FragmentManager popBackStackImmediate()方法,如果直接掉finish 就不会触发异常,但这种情况只建议在没有使用 Fragments api时调用。
可以加个标志位
The exception is threw here (In FragmentActivity):
@Override
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
super.onBackPressed();
}
}
InFragmentManager.popBackStatckImmediate(),FragmentManager.checkStateLoss()is called firstly. That's the cause ofIllegalStateException. See the implementation below:
private void checkStateLoss() {
if (mStateSaved) { // Boom!
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
I solve this problem simply by using a flag to mark Activity's current status. Here's my solution:
public class MainActivity extends AppCompatActivity {
/**
* A flag that marks whether current Activity has saved its instance state
*/
private boolean mHasSaveInstanceState;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
mHasSaveInstanceState = true;
super.onSaveInstanceState(outState);
}
@Override
protected void onResume() {
super.onResume();
mHasSaveInstanceState = false;
}
@Override
public void onBackPressed() {
if (!mHasSaveInstanceState) {
// avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException
super.onBackPressed();
}
}
}
https://stackoverflow.com/questions/7469082/getting-exception-illegalstateexception-can-not-perform-this-action-after-onsa
http://blog.csdn.net/edisonchang/article/details/49873669