今天在运行项目的时候突然出了如标题的错误,大写的懵逼!!!然后,将 FragmentTransaction 的 commit() 方法换成了 commitAllowingStateLoss 就OK了(不过下文中会告诉你不建议这么做),不过,不明所以,就花了一些时间看了看源码,并且找了几篇还不错的博客,记录在此。
想了想,还是先从现象说起吧,以倒推的方式来进行,这个其实比较直观一些。
现象:就如大部分app一样,首页会有一个底部的导航栏,在点击设置的menu的时候,如果没有登录就让用户去登录,然后登录成功后会在 MainActivity 的 onActivityResult 中动态添加一个设置的 fragment,那么问题就来了,就在添加后并且 commit 后报错了。
解决:将 FragmentTransaction 的 commit() 方法换成了 commitAllowingStateLoss 。
源码理解: FragmentTransaction的commit和commitAllowingStateLoss的区别
如果不耐烦看整篇博客的话可以直接看到最后的总结:ok,到这里,真相总算大明,当使用commit方法时,系统将进行状态判断,如果状态(mStateSaved)已经保存,将发生”Can not perform this action after onSaveInstanceState”错误。
这里我就不贴源码了,毕竟在上篇博客中已经很全了(而且复制别人写过的东西感觉很没意思啊,感谢作者)。
总之如果看到这里,其实问题基本就可以解决了,至少项目不会报错了,不过,我是真的不太明白为什么会这样,这到底是什么意思!
深入的理解
不是很理解源码中判断的保存到底是指的什么。一开始看到博客中的总结,以为是指 fragment 的保存,这其实就是个很大的错误,我想了一下就否决掉了,毕竟按照我的逻辑而言设置的 fragment 是绝对没有保存过得,除非我的 MainActiivty 销毁重建了,不过那样的话导致的错误也就不是标题的错误了,而是 fragment 重叠的问题了。
于是真的找到了一篇让我读了不下于五遍的博客(没错,我就是传说中的菜鸟程序猿=-=)Fragment Transactions和Activity状态丢失,万分感谢作者让我学到了不少。
如果你和我一样读懂了这篇文章那么就不用看我下面说的了,如果不是很懂,那么就可以看看我的总结是否正确:上文中所谓的保存,其实指的是Activity的保存,那么自然而然就想到了 onSaveInstance 方法,这个方法就是用来保存 Activity 状态的。可是这和 onSaveInstance 有什么关系呢?在聊关系之前,先了解下 onSaveInstance 是在什么时候调用的 activity 中的 onSaveInstanceState方法的调用时机,很明显除了 home 键选择程序/ home 键退到后台,我的情况属于从当前的 Activity 跳转到另外一个 Activity,那么 onSaveInstance 自然就调用了。
结合 状态丢失 的这篇博文,可以知道 onSaveInstance 是在 onPose 和 onStop 之间调用的,google认为状态丢失是一件非常重要的事情,文中说不惜一切代价的告诉用户,这一点我从直接抛出异常就体验到了,宁愿异常,也不愿意放过这个隐藏的问题。那么在 Activity 的onStop 方法调用之前就已经把状态保存了,那么在 onActivityResult 中的commit 方法自然就无法被保存起来了,异常就是这么来的。(建议可以认真看完 状态丢失 这篇博文,还可以了解到 3.0 前后 Activity 的一些变化)
真正的解决办法:在文章的最后三个建议中,其实真正的解决方法就是 建议一 ,建议在在FragmentActivity的onResumeFragments()函数或者Activity的onPostResume()函数中提交,而建议二,其实可以算是一个忠告,建议三就是不建议使用的,这种方法忽略了用户体验。