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
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值