Can not perform this action after onSaveInstanceState Fragment切换问题解决办法(千万别用replace 替换)

本文解决在Android应用中,使用Fragment时出现的java.lang.IllegalStateException异常。错误发生在尝试在onSaveInstanceState之后添加Fragment,通过使用commitAllowingStateLoss替代commit,并调整Fragment的添加方式,成功解决了问题。

最近项目在内测的时候没有任何问题, 但是在发布正式版的时候老是报错: 并且一直不给出具体的错误代码,不能详细的定位在某一行.

 

问题描述:

#7522 java.lang.IllegalStateException
Can not perform this action after onSaveInstanceState
android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1842)

  详细信息:

android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1842)
2 android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1860)
3 android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:650)
4 android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:609)
5 android.support.v4.app.FragmentTabHost.onAttachedToWindow(FragmentTabHost.java:288)
6 android.view.View.dispatchAttachedToWindow(View.java:13414)
7 android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2708)
8 android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2715)
9 android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2715)
10 android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2715)
11 android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2715)
12 android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1347)
13 android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1082)
14 android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5883)
15 android.view.Choreographer$CallbackRecord.run(Choreographer.java:838)
16 android.view.Choreographer.doCallbacks(Choreographer.java:637)
17 android.view.Choreographer.doFrame(Choreographer.java:603)
18 android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:824)
19 android.os.Handler.handleCallback(Handler.java:739)
20 android.os.Handler.dispatchMessage(Handler.java:95)
21 android.os.Looper.loop(Looper.java:135)
22 android.app.ActivityThread.main(ActivityThread.java:5305)
23 java.lang.reflect.Method.invoke(Native Method)
24 java.lang.reflect.Method.invoke(Method.java:372)
25 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:922)
26 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:717)

 Bugly给出的解决办法:

解决方案
状态异常。
java.lang.IllegalStateException异常产生的原因及解决办法  

错误类型大致为以下几种:
java.lang.IllegalStateException:Cannot   forward   a   response   that   is   already   committed 
IllegalStateException:response already commited 
IllegalStateException:getOutputStream() has already been called for this request
…………
=========================================================
IllegalStateException: Can not perform this action after onSaveInstanceState:
#解决办法:onSaveInstanceState方法是在该Activity即将被销毁前调用,来保存Activity数据的,如果在保存玩状态后
再给它添加Fragment就会出错。解决办法就是把commit()方法替换成 commitAllowingStateLoss()
=========================================================
错误原因:
  该异常表示,当前对客户端的响应已经结束,不能在响应已经结束(或说消亡)后再向客户端(实际上是缓冲区)输出任何内容。
  Object is no longer valid to operate on. Was it deleted by another thread?
  该异常表示,realmObject对象在其他线程已被删除,在这个线程中使用的时候抛出的异常。

具体分析:

首先解释下flush(),我们知道在使用读写流的时候数据先被读入内存这个缓冲区中, 然后再写入文件,但是当数据读完时不代表数据已经写入文件完毕,因为可能还有一部分仍未写入文件而留在内存中,这时调用flush()方法就会把缓冲区的数据强行清空输出,因此flush()的作用就是保证缓存清空输出。response是服务端对客户端请求的一个响应,其中封装了响应头、状态码、内容等,服务端在把response提交到客户端之前,会向缓冲区内写入响应头和状态码,然后将所有内容flush。这就标志着该次响应已经committed(提交)。对于当前页面中已经committed(提交)的response,就不能再使用这个response向缓冲区写任何东西(注:同一个页面中的response.XXX()是同一个response的不同方法,只要其中一个已经导致了committed,那么其它类似方式的调用都会导致 IllegalStateException异常)。
参考:http://my.oschina.net/guhai2004/blog/187041,https://github.com/realm/realm-java/issues/1206

补充另一种异常情况:
我这里的异常是:
java.lang.IllegalStateException
Can't change tag of fragment d{e183845 #0 d{e183845}}: was d{e183845} now d{e183845 #0 d{e183845}}
经查,我在显示fragment的代码中使用了:
fragment.show(getSupportFragmentManager, fragment.toString());
而这里是因为两次toString()结果不同,导致不同的tag指向的是同一个fragment。
获取fragment的tag的正确方法应该是使用其提供的fragment.getTag()方法。

=====================================================
补充异常:
java.lang.IllegalStateException
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 37 path $.data
错误原因:该异常是由于服务器错误返回的JSON字符串和服务器正常下时返回的JSON字符串结构不同,导致利用Gson解析的时候报了一个异常:本该去解析集合却强制去解析对象所致.
解决办法:在使用Gson解析JSON时try cash一下,不报错按照正常逻辑继续解析,报异常则处理为请求失败逻辑即可.
======================================================

 看到这里,按照上面的方式,我把所有的代码都改为commitAllowingStateLoss();   上线后,还是时不时的代码报错!

 后来经过多方面查找,错误代码如下:

  //初始化
    private void initFragmentManager() {
        fragmentManager = getSupportFragmentManager();
        transaction = fragmentManager.beginTransaction();
        homeFragment = new ChildHomeFragment();
        transaction.replace(R.id.content, homeFragment, ChildHomeFragment.TAG);//现将首页放进transaction
        fragmentTag = ChildHomeFragment.TAG;
        transaction.addToBackStack(null);
//        transaction.commit();
        transaction.commitAllowingStateLoss();
    }

 transaction.replace(R.id.content, homeFragment, ChildHomeFragment.TAG);

其实错误代码就是这一行!!!!

 运行项目的时候,用到的replace ()  用到这个方法就导致了无法定位具体的错误,这个方法本身就有问题.

后续解决方式:

   private void initFragmentManager() {
        fragmentTag = ChildHomeFragment.TAG;
        fragmentManager = getSupportFragmentManager();
        ft = fragmentManager.beginTransaction();
        fragment = fragmentManager.findFragmentByTag(ChildHomeFragment.TAG);
        fragment = new ChildHomeFragment();
        ft.add(R.id.content, fragment, fragmentTag);//采用add的方式解决
        ft.addToBackStack(null);
        ft.commitAllowingStateLoss();
    }

更新发布后,问题得到解决,后续没有继续包上面的错误!

希望能帮到遇到类似问题的朋友! android API中不一定没有坑,这个坑就是我遇到的!

 

### 回答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: onSaveInstanceState是Android中一个很重要的生命周期方法之一,它通常在Activity或Fragment即将被销毁之前被调用,用于保存Activity或Fragment的状态,以便在Activity或Fragment被重建时恢复它们的状态。在这个方法被调用后,Activity或Fragment的状态已经被保存,如果继续进行操作,可能会导致状态丢失或不一致。 因此,如果在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的实例,以避免重建时出现状态丢失的问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值