解决 IllegalStateException: Can not perform this action after onSaveInstanceState

了解异常如何产生,首先必须弄明白onSaveInstanceState方法的调用时机,

概括的讲,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保存状态。

那么,为什么会抛出异常呢?原因在于我们的activity在某种场景下处于被kill 掉的边缘,系统就调用了onSaveInstanceState 方法,这个方法里面会调用 FragmentManager saveAllState 方法,将fragment 的状态保存,在状态保存后用户又主动调了 onBackPressed ,而这个方法的超类super.onBackPressed 方法会判断FragmentManager 是否保存了状态,如果已经保存就会抛出IllegalStateException 的异常 。
这一类异常原因是在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 方法被调用之前,可能依然保存着之前的状态导致异常 。
(2)尽可能避免在一些和生命周期函数异步的方法中调用commit,如AsyncTask 等。 
(3)实在没法确定调用时机时,可以用commitAllowingStateLoss 代替 commit ,commitAllowingStateLoss 在状态丢失时不会抛出任何异常,但也正因为如此在一些必须确保状态被保存的场合,最好不要使用 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时调用。
 

你可能感兴趣的:(安卓,android)