解决Can not perform this action after onSaveInstanceState异常总结

转发请标明来源:http://blog.csdn.net/rflyee/article/details/74723891

上篇博客(这里这里)从源码层面分析了Can not perform this action after onSaveInstanceState异常产生的原因及流程,

接下来分析下该崩溃的设计原因以及如何避。

该异常的设计原因

        大家都知道系统在内存吃紧时会按规则优先kill掉部分非前台activity,为了保证用户体验,系统在kill掉某个activity之前会先调用onSaveInstanceState,将当前window的一些重要状态以bundle的形式持久化,当用户回到该activity时,再通过onCreate、onRestoreInstanceState等方法恢复之前的状态,给用户一种该界面从来没有被kill的假象,从而提升用户体验。

    那么问题来了,在系统onSaveXXX保存状态之后,一直到onRestoreXXX恢复状态之前,这段时间里程序如果再操作window,比如操作FragmentTransaction#commit()、操作FragmentDialog#show()/dismiss(),这些操作肯定没有保存在Bundle中,也就是下次onRestoreXXX恢复状态时是没有这些操作的,也就是所谓的lose state丢失状态,这样可能给用户一种前后不一致的用户体验。所以安卓团队为了“提升用户体验”,saveState之后再操作window就抛出一个IllegalStateException 异常,提醒(逼迫)开发者注意喽。

异常出现时机

如果细心可能会发现,不同的系统异常抛出的时机是不一样的。(还记得上篇文章源码中的saveAllState()方法中改变mStateSaved变量的判断了吗)
原因是3.x之后由于内存吃紧系统主动kill Activity的时机发生了变化,导致onSaveInstanceState的调用时机发生了变化。
变化如下:
    3.x之前:
 onPause()之后activity可能被系统kill。因此:(optional)onSaveInstanceState() -- onPause()
    3.x之后:
    onStop()之后activity可能被系统kill。因此:(optional)onSaveInstanceState() -- onStop()

注意以上高亮的(optional)可选,即并不是说onPause/onStop之前一定调onSaveXXX,而是根据情况有可能调用,详情见这里这里

因此,如果按照正常的设计,在3.x之前的系统onPause()之后如果再进行FragmentTransaction#commit()等操作则会导致崩溃,你懂的,那程序崩的几率可是杠杠的了。因此为了保证更好的用户交互体验,安卓团队做出了妥协,即3.x之前的系统,如果在onPause() - onStop()之间进行FragmentTransaction#commit()类似操作,即使状态丢失也不抛异常,但是如果onStop()之后再进行此类操作则直接抛异常。

引用Alex Lockwood的描述即:

pre-Honeycomb
post-Honeycomb
commit() before onPause()OK
OK
commit() between onPause() and onStop()STATE LOSS
OK
commit() after onStop()EXCEPTION
EXCEPTION

解决方法

理解了异常的原因,就明白我们只能在saveSate之前、restoreState之后进行commit transaction。总结如下
(1)onStop(准确说是onSaveInstanceState)之后不能再执行此类操作,如FragmentTransaction#commit()。因此,可以考虑自己在onSaveInstanceState中添加一个boolean flag,但记得合适时候(onRestoreXXX、oncreate、onPostresume、onResumeFragment)将其reset
(2)在生命周期方法中(除onCreate())commit transactions ,要在FragmentActivity#onResumeFragments() 或者 Activity#onPostResume()里使用。
    RragmentAvtivity#onResume不可以,可以用FragmentAvtivity#onResumeFragments 因为调用onResume时不能保证已经restore之前的state了(假设之前被kill,并saveState),参见 documentation
(3)尽量不要在异步回调中修改状态(commit transaction)。
    比如,activity中执行一个AsyncTask --- 按手机Home回到桌面 --- AsyncTask执行完毕回调onPostExecute() 
--- onPostExecute中进行修改状态 --- 异常。
    同理view.post(runnable) view.postDelay(runnable)都可能出现此情况。
(4)最后的最后,Fragment使用commitAllowingStateLoss代替commit()。前者允许状态丢失,其他完全一样。也即控制着源码中allowStateLoss
(5)有blog说覆写onSaveInstanceState()但是不调用super.onSaveInstanceState(outState);也能避免崩溃,知道原因后的你肯定灰常不建议这么做了(不然onSaveInstanceState还有啥用)。(个人测试,奇酷手机青春版,Android5.1,此方法无效
@Overrideprotectedvoid onSaveInstanceState(Bundle outState){//No call for super(). Bug on API Level > 11.}

试试这样解决,在BaseActivity中添加:
@Override
protected void onStart() {
    super.onStart();
    // super.onStart();中将mStateSaved置为false
    mStateEnable = true;
}
@Override
protected void onResume() {
	// onPause之后便可能调用onSaveInstanceState,因此onresume中也需要置true
	mStateEnable = true;
	super.onResume();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
	// super.onSaveInstanceState();中将mStateSaved置为true
	mStateEnable = false;
	super.onSaveInstanceState(outState);
}
@Override
protected void onStop() { 
	// super.onStop();中将mStateSaved置为true 
	mStateEnable = false;
	super.onStop();
}
/** 
 * 
 * activity状态是否处于可修改周期内,避免状态丢失的错误 
 * @return 
 */
public boolean isStateEnable() {
	return mStateEnable;
}
之后调用修改状态的方法如fragmentDialog的show()、dismiss()方法等,则先判断状态是否可用。
if(isStateEnable()) {
	dialog.show();
}
  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个错误是因为在 onSaveInstanceState() 方法之后尝试执行某些操作,这是不允许的。 onSaveInstanceState() 方法是在 Activity 即将被销毁之前调用的,它的主要作用是保存 Activity 的状态信息,以便在 Activity 重新创建时恢复状态。因此,在 onSaveInstanceState() 方法之后执行某些操作可能会导致状态信息丢失或不一致。如果需要在 onSaveInstanceState() 方法之后执行某些操作,可以考虑将这些操作放在 onResume() 方法中执行。 ### 回答2: 在Android开发中,当我们的应用程序遇到设备旋转或配置更改等情况时,系统可能会销毁并重建活动,以适应新的设备状态。在这种情况下,在 onSaveInstanceState() 方法中,我们通常将一些重要的应用程序状态保存到 Bundle 对象中,以便在活动重新创建时进行恢复。但是,在 onSaveInstanceState() 方法调用后,一些操作可能会受到限制,因为活动实际上已被销毁。其中包括使用 FragmentTransaction 进行 Fragment 操作,或者提交 AsynTask 等异步操作。如果在此时尝试执行此类操作,则会导致崩溃或其他不可预测的结果。 如果您需要在 onSaveInstanceState() 方法之后执行某些操作,您可以使用简单的技巧来避免出现错误。一种方法是使用 Handler 来执行延迟操作。使用此方法,您可以先在 onSaveInstanceState() 方法中将 Runnable 对象传递给 Handler,并设置合适的延迟时间。当 Runnable 对象在指定时间后在主线程上执行时,Activity 已经重建,所以不存在任何问题。 另一个解决方案是使用 onSaveInstanceState() 方法的一种变体,即 onSaveInstanceState(Bundle, PersistableBundle) 方法。该方法与 onSaveInstanceState(Bundle) 方法不同的是,它还接收一个 PersistableBundle 参数,该参数可以在拥有足够空间的设备上持久化保存。这样,即使活动被销毁并重建,也可以恢复 PersistableBundle 中保存的状态。 总之,在 onSaveInstanceState() 方法被调用后,您应避免执行可能影响 Activity 生命周期的任何操作。如果确实需要执行此类操作,请使用上述技巧来确保正确性和稳定性。 ### 回答3: onSaveInstanceStateAndroid中一个很重要的生命周期方法之一,它通常在ActivityFragment即将被销毁之前被调用,用于保存ActivityFragment的状态,以便在ActivityFragment被重建时恢复它们的状态。在这个方法被调用后,ActivityFragment的状态已经被保存,如果继续进行操作,可能会导致状态丢失或不一致。 因此,如果在onSaveInstanceState方法被调用后再尝试执行某些操作,就会出现 can not perform this action after onSaveInstanceState 的错误信息。 例如,在Activity的onSaveInstanceState方法被调用后,如果尝试在onPause方法中执行一个Fragment的事务,就会出现这个错误。这是因为在onSaveInstanceState方法被调用后,Fragment的状态已经被保存,并且任何与Fragment相关的操作都不应该被执行,以避免状态丢失或不一致。 为了避免出现can not perform this action after onSaveInstanceState的错误,可以在onSaveInstanceState方法被调用后,避免执行任何与状态相关的操作。如果确实需要在onSaveInstanceState之后执行某些操作,可以考虑使用Handler或post方法进行延迟执行,以确保状态已经被保存并恢复,而不会出现错误。另外,可以尝试使用Fragment的setRetainInstance方法保留Fragment的实例,以避免重建时出现状态丢失的问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值