解决java.lang.IllegalStateException: Can not perform this action after onSaveInstance

1. 本人某个android项目开发阶段一直运行良好,直到上线前夕,在某款跑着android 4.03系统的手机运行却报出一下异常,导致force close:java.lang.IllegalStateException: Can not perform this action after onSaveInstance!

首先得了解一下我那项目的一些基本情况,UI结构是TabActivity包含着5个Tabs,每个tab又是一个独立的Activity。

异常是发生在android 4.03系统上,当我在某个Tab上按Back键时,就会报出java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

从logout里发现了整个异常发生的过程:

Java代码   收藏代码
  1. java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState  
  2. at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)  
  3. at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)  
  4. at android.app.Activity.onBackPressed(Activity.java:2066)  
  5. at android.app.Activity.onKeyUp(Activity.java:2044)  
  6. at android.view.KeyEvent.dispatch(KeyEvent.java:2529)  
  7. at android.app.Activity.dispatchKeyEvent(Activity.java:2274)  
  8. at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)  
  9. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  10. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  11. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  12. at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)  
  13. at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)  
  14. at android.app.Activity.dispatchKeyEvent(Activity.java:2269)  
  15. at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)  
  16. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  17. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  18. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  19. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  20. at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)  
  21. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  22. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  23. at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)  
  24. at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)  
  25. at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)  
  26. at android.app.Activity.dispatchKeyEvent(Activity.java:2269)  
  27. at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)  
  28. at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)  
  29. at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)  
  30. at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)  
  31. at android.os.Handler.dispatchMessage(Handler.java:99)  
  32. at android.os.Looper.loop(Looper.java:132)  
  33. at android.app.ActivityThread.main(ActivityThread.java:4028)  
  34. at java.lang.reflect.Method.invokeNative(Native Method)  
  35. at java.lang.reflect.Method.invoke(Method.java:491)  
  36. at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)  
  37. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)  
  38. at dalvik.system.NativeStart.main(Native Method)  

上面的异常信息表示,我写的类不是异常的源头。根据异常信息Can not perform this action after onSaveInstanceState,可以了解到异常原因:在onSaveInstanceState行为之后,app执行某个不能响应的行为而导致异常发生。

在信息at android.app.Activity.onBackPressed(Activity.java:2066),这一句表明异常是在响应返回键响应事件的行为上发生的。我们顺藤摸瓜,考究一下在我们按下返回键时,activity会执行的响应:onKeyDown-->onBackPressed-->onPause->onStop->onDestroy。

那导火索onSaveInstanceState又是在什么时候执行的?

我们先看android API的一段原文:

Java代码   收藏代码
  1. 先看Application Fundamentals上的一段话:  
  2.    
  3.       Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action   
  4. (such as pressing the BACK key)  

从上面可以知道,当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。

注意上面的双引号,何为“容易”?言下之意就是该activity还没有被销毁,而仅仅是一种可能性。

onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据。

那为什么项目里头响应onBackPressed事件时会报出上面的异常呢,还表明是after onSaveInstanceState?

原因是我Tab里面的Activity响应了onBackPressed事件,得弹出task,作为它的父容器TabActivity当然也得弹出task,TabActivity 变得“容易”被系统销毁,于是就调用onSaveInstanceState保存状态。

现在整个流程都明白了,可是,这一切都很正常啊,这个流程也很符合Activity的生命周期啊,为什么还会报异常呢?还是在最新的android 4.03上出问题,难道是说,系统不兼容?

对!

经过一番网上查阅,发现API 11 以上某些控件,包括 Fragment还有ActivityGroup,在调用saveInstanceState存在Bug,可能是google对saveInstanceState的实现做过修改。

直到隐藏在后面的原因,解决问题的思路就出来了:让父容器TabActivity在不调用saveInstanceState的情况下onDestroy

具体思路在tab上面的activity监听BACK键的事件,响应并拦截,再通过广播方式通知父容器TabActivity,主动销毁自己,达到原来响应onBackPressed退出App的效果。


2.做项目的时候用到fragment,在fragment快速切换的时候发生了异常:IllegalStateException: Can not perform this action after onSaveInstanceState

logcat的提示信息: Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState


这个异常是由于在使用FragmentTransition的 commit方法添加一个Fragment的时候出现的

public abstract int commit ()

Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready.

A transaction can only be committed with this method prior to its containing activity saving its state. If the commit is attempted after that point, an exception will be thrown. This is because the state after the commit can be lost if the activity needs to be restored from its state. See commitAllowingStateLoss() for situations where it may be okay to lose the commit.

Returns
  • Returns the identifier of this transaction's back stack entry, if addToBackStack(String) had been called. Otherwise, returns a negative number.
开始我用的是这个方法,fragment切换的时候基本上都会用这个方法。以前用的时候没出错,这次却不行了。在commit()方法的后边还有个方法:

public abstract int commitAllowingStateLoss ()

Like commit() but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.

这个方法与commit()一样,但是允许在activity的状态信息保存后调用。用这个方法是不安全的,有可能activity还没恢复呢就不执行commit方法了。所以这个方法只能用在ui状态转换意外的时候

比较两个方法的不同就可以知道,我调用的 commit方法是在Activity的onSaveInstanceState()之后调用的。onSaveInstanceState方法是在该Activity即将被销毁前调用,来保存Activity数据的,如果在保存完状态后
再给它添加Fragment就会出错。解决办法就是把commit()方法替换成 commitAllowingStateLoss()就行了。



3.使用Fragment遇到的异常:

1.关于Fragment(XXFragment) not attached to Activity 异常出现该异常,是因为Fragment的还没有Attach到Activity时,调用了如getResource()等,需要上下文Content的函数。解决方法,就是等将调用的代码写在OnStart()中。网上还有几处这样的参考:http://stackoverflow.com/questions/10919240/fragment-myfragment-not-attached-to-activity  回答的主要是在调用

[java]  view plain copy
  1. getResources().getString(R.string.app_name);  
之前增加一个判断isAdded(),两外说这个异常解决办法的有 http://stackoverflow.com/questions/6870325/android-compatibility-package-fragment-not-attached-to-activity
这个是针对另外一种情况下的解决方式。
2.在使用Fragment保存参数的时候,可能是因为需要保存的参数比较大或者比较多,这种情况下页会引起异常。比如代码
[java]  view plain copy
  1. Bundle b = new Bundle();  
  2. b.putParcelable("bitmap", bitmap2);  
  3. imageRecognitionFragment.setArguments(b);  
设置好参数,并且添加hide(),add(),方法之后,需要commit(),来实现两个Fragment跳转的时候,这种情形下参数需要进行系统保存,但是这个时候你已经实现了跳转,系统参数却没有保存。此时就会报
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
异常。分析原因:你并不需要系统保存的参数,只要你自己设置的参数能够传递过去,在另外一个Fragment里能够顺利接受就行了,现在android里提供了另外一种形式的提交方式commitAllowingStateLoss(),从名字上就能看出,这种提交是允许状态值丢失的。到此问题得到完美解决,值的传递是你自己控制的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值