一个常见Android崩溃问题的分析

       这个错误大家应该都有遇到过:IllegalStateException: Can not perform this action after onSaveInstanceState。最近又遇到这个问题,就想要梳理一下其中的一些细节。

       我的具体使用场景是在一个AsyncTask的回调onPostExecute中调用了FragmentTransaction的commit方法,并且在调用commit方法之前调用了Activity的isDestroyed方法进行保护。代码如下:

一个常见Android崩溃问题的分析_第1张图片

       这里的本意是要判断Activity是否已经在销毁了,是的话就不再调用commit方法。但是这里的代码有两个问题,首先在AsyncTask的回调中调用commit是不安全的,应该要调用commitAllowingStateLoss,其次,这里虽然通过判断Activity是否destroy做了保护,但这个保护也是不准确的。

        首先来看第一个问题:为什么在AsyncTask的回调中调用commit是不安全的?

       首先我们要找到这个方法实现的地方,FragmentTransaction的commit方法实际上是由BackStackRecord实现的,我们一步步查看内部的调用:commit -> commitInternal -> mManager.enqueueAction -> checkStateLoss。

一个常见Android崩溃问题的分析_第2张图片

一个常见Android崩溃问题的分析_第3张图片

一个常见Android崩溃问题的分析_第4张图片

一个常见Android崩溃问题的分析_第5张图片

       我们看到在checkStateLoss方法里mStateSaved为true的时候抛出了这个异常,需要找到mStateSaved是在哪里被置为true的。

一个常见Android崩溃问题的分析_第6张图片

 

一个常见Android崩溃问题的分析_第7张图片

       我们发现mStateSaved是在saveAllState方法中被设置的,而saveAllState又是在Activity的onSaveInstanceState中调用的。也就是说当系统调用了onSaveInstanceState之后,我们再去调用FragmentTransaction的commit方法,系统就会抛出上面的异常。

       对于这里Android系统选择抛出异常,我的理解是Activity是在onSaveInstanceState方法去保存状态的,如果我们在onSaveInstanceState之后去执行FragmentTransaction的commit,Fragment的状态就不会保存,导致意外丢失,这种情况属于不可预期情况下丢失了状态,所以系统才会通过抛异常通知开发者进行处理。

        那为什么调用commitAllowingStateLoss不会抛异常呢?方法的调用栈依然和上面相同,commit -> commitInternal -> mManager.enqueueAction ,只是这里传的参数是true,也就是说程序不会走到 checkStateLoss的分支,也就不会抛出开始的那个异常。而且我们看到在Fragment已经destroy或者detach的情况下,方法会直接return掉,即会丢掉当次fragment状态的变化而不进行保存。

       再来看第二个问题:为什么在调用commit方法之前调用Activity的isDestroyed方法来判断是不准确的。

       我们通过源码来看一下,mDestroyed标志是在什么时候设置的。mDestroyed是在performDestroy方法中设为true的,而performDestroy则是在Activity即将调用onDestroy的时候被调用的。也就是说,如果我们在mDestroy是false的时候调用了FragmentTransaction的commit方法,由于onSaveInstanceState的调用会在onDestroy之前,mStateSaved可能已经被修改为true了,所以是存在异常风险的。

       怎么才能解决这个问题呢?

       其实有了前面的分析,我们可以考虑继承onSaveInstanceState方法,然后设置一个变量,如果这个变量被修改了,说明onSaveInstanceState方法被系统调用过,那么这时候再去调用FragmentTransaction的commit方法就是不安全的了。

       上文通过一个常见的崩溃,引申出Fragment在某些情况下的处理方法,一方面可以避免我们在工作中遇到同样的问题,另一方面也可以加深我们对Fragment的一些原理的理解。

 

欢迎关注我的微信公众号,收到最新的推送文章

 

一个常见Android崩溃问题的分析_第8张图片

你可能感兴趣的:(【Android】)