使用Fragment的时候,出现了这个错误IllegalStateException: Can not perform this action after onSaveInstanceState

public abstract int commit ()Added in API level 11Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready.A transaction can only be committed with this method prior to its containing activity saving its state. If the commit is attempted after that point, an exception will be thrown. This is because the state after the commit can be lost if the activity needs to be restored from its state. See commitAllowingStateLoss() for situations where it may be okay to lose the commit.ReturnsReturns the identifier of this transaction's back stack entry, if addToBackStack(String) had been called. Otherwise, returns a negative number.

public abstract int commitAllowingStateLoss ()Added in API level 11Like commit() but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.

对比这2个方法,大概知道原因了。

commitAllowingStateLoss()真的能安全的处理这个问题么?后续影响待以后遇到再说。

The following stack trace and exception message has plagued StackOverflow ever since Honeycomb's initial release:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

This post will explainwhyandwhenthis exception is thrown, and will conclude with several suggestions that will help ensure it never crashes your application again.

Why was the exception thrown?

The exception was thrown because you attempted to commit aFragmentTransactionafter the activity's state had been saved, resulting in a phenomenon known asActivity state loss. Before we get into the details of what this actually means, however, let's first take a look at what happens under-the-hood whenonSaveInstanceState()is called. As I discussed in my last post aboutBinders & Death Recipients, Android applications have very little control over their destiny within the Android runtime environment. The Android system has the power to terminate processes at any time to free up memory, and background activities may be killed with little to no warning as a result. To ensure that this sometimes erratic behavior remains hidden from the user, the framework gives each Activity a chance to save its state by calling itsonSaveInstanceState()method before making the Activity vulnerable to destruction. When the saved state is later restored, the user will be given the perception that they are seamlessly switching between foreground and background activities, regardless of whether or not the Activity had been killed by the system.

When the framework callsonSaveInstanceState(), it passes the method aBundleobject for the Activity to use to save its state, and the Activity records in it the state of its dialogs, fragments, and views. When the method returns, the system parcels theBundleobject across a Binder interface to the System Server process, where it is safely stored away. When the system later decides to recreate the Activity, it sends this sameBundleobject back to the application, for it to use to restore the Activity's old state.

So why then is the exception thrown? Well, the problem stems from the fact that theseBundleobjects represent a snapshot of an Activity at the momentonSaveInstanceState()was called, and nothing more. That means when you callFragmentTransaction#commit()afteronSaveInstanceState()is called, the transaction won't be remembered because it was never recorded as part of the Activity's state in the first place. From the user's point of view, the transaction will appear to be lost, resulting in accidental UI state loss. In order to protect the user experience, Android avoids state loss at all costs, and simply throws anIllegalStateExceptionwhenever it occurs.

When is the exception thrown?

If you've encountered this exception before, you've probably noticed that the moment when it is thrown is slightly inconsistent across different platform versions. For example, you probably found that older devices tended to throw the exception less frequently, or that your application was more likely to crash when using the support library than when using the official framework classes. These slight inconsistencies have led many to assume that the support library is buggy and can't be trusted. These assumptions, however, are generally not true.

The reason why these slight inconsistencies exist stems from a significant change to the Activity lifecycle that was made in Honeycomb. Prior to Honeycomb, activities were not considered killable until after they had been paused, meaning thatonSaveInstanceState()was called immediately beforeonPause(). Beginning with Honeycomb, however, Activities are considered to be killable only after they have beenstopped, meaning thatonSaveInstanceState()will now be called beforeonStop()instead of immediately beforeonPause(). These differences are summarized in the table below:

pre-Honeycomb post-Honeycomb
Activities can be killed beforeonPause()? NO NO
Activities can be killed beforeonStop()? YES NO
onSaveInstanceState(Bundle)is guaranteed to be called before... onPause() onStop()

As a result of the slight changes that were made to the Activity lifecycle, the support library sometimes needs to alter its behavior depending on the platform version. For example, on Honeycomb devices and above, an exception will be thrown each and every time acommit()is called afteronSaveInstanceState()to warn the developer that state loss has occurred. However, throwing an exception every time this happened would be too restrictive on pre-Honeycomb devices, which have theironSaveInstanceState()method called much earlier in the Activity lifecycle and are more vulnerable to accidental state loss as a result. The Android team was forced to make a compromise: for better inter-operation with older versions of the platform, older devices would have to live with the accidental state loss that might result betweenonPause()andonStop(). The support library's behavior across the two platforms is summarized in the table below:

pre-Honeycomb post-Honeycomb
commit()beforeonPause() OK OK
commit()betweenonPause()andonStop() STATE LOSS OK
commit()afteronStop() EXCEPTION EXCEPTION

How to avoid the exception?

Avoiding Activity state loss becomes a whole lot easier once you understand what is actually going on. If you've made it this far in the post, hopefully you understand a little better how the support library works and why it is so important to avoid state loss in your applications. In case you've referred to this post in search of a quick fix, however, here are some suggestions to keep in the back of your mind as you work withFragmentTransactions in your applications:

  • Be careful when committing transactions inside Activity lifecycle methods.A large majority of applications will only ever commit transactions the very first timeonCreate()is called and/or in response to user input, and will never face any problems as a result. However, as your transactions begin to venture out into the other Activity lifecycle methods, such asonActivityResult(),onStart(), andonResume(), things can get a little tricky. For example, you should not commit transactions inside theFragmentActivity#onResume()method, as there are some cases in which the method can be called before the activity's state has been restored (see thedocumentationfor more information). If your application requires committing a transaction in an Activity lifecycle method other thanonCreate(), do it in eitherFragmentActivity#onResumeFragments()orActivity#onPostResume(). These two methods are guaranteed to be called after the Activity has been restored to its original state, and therefore avoid the possibility of state loss all together. (As an example of how this can be done, check out my answer tothis StackOverflow questionfor some ideas on how to commitFragmentTransactions in response to calls made to theActivity#onActivityResult()method).

  • Avoid performing transactions inside asynchronous callback methods.This includes commonly used methods such asAsyncTask#onPostExecute()andLoaderManager.LoaderCallbacks#onLoadFinished(). The problem with performing transactions in these methods is that they have no knowledge of the current state of the Activity lifecycle when they are called. For example, consider the following sequence of events:

    1. An activity executes anAsyncTask.
    2. The user presses the "Home" key, causing the activity'sonSaveInstanceState()andonStop()methods to be called.
    3. TheAsyncTaskcompletes andonPostExecute()is called, unaware that the Activity has since been stopped.
    4. AFragmentTransactionis committed inside theonPostExecute()method, causing an exception to be thrown.

    In general, the best way to avoid the exception in these cases is to simply avoid committing transactions in asynchronous callback methods all together. Google engineers seem to agree with this belief as well. According tothis poston the Android Developers group, the Android team considers the major shifts in UI that can result from committingFragmentTransactions from within asynchronous callback methods to be bad for the user experience. If your application requires performing the transaction inside these callback methods and there is no easy way to guarantee that the callback won't be invoked afteronSaveInstanceState(), you may have to resort to usingcommitAllowingStateLoss()and dealing with the state loss that might occur. (See also these two StackOverflow posts for additional hints,hereandhere).

  • UsecommitAllowingStateLoss()only as a last resort.The only difference between callingcommit()andcommitAllowingStateLoss()is that the latter will not throw an exception if state loss occurs. Usually you don't want to use this method because it implies that there is a possibility that state loss could happen. The better solution, of course, is to write your application so thatcommit()is guaranteed to be called before the activity's state has been saved, as this will result in a better user experience. Unless the possibility of state loss can't be avoided,commitAllowingStateLoss()should not be used.

Hopefully these tips will help you resolve any issues you have had with this exception in the past. If you are still having trouble, post a question onStackOverflowand post a link in a comment below and I can take a look. :)

更多相关文章

  1. android开发过程遇到的问题和解决方案(不断更新)
  2. Android(安卓)Studio使用过程中遇到的问题(一)-- "value 2"异常解
  3. [转]android Android(安卓)SDK Setup的使用及遇到的问题,Faild t
  4. 项目中遇到的问题留下笔记
  5. Android与标准Linux对比
  6. Android初次使用遇到的两个问题
  7. [转]android Android(安卓)SDK Setup的使用及遇到的问题,Faild t
  8. 【zz】安装Android的Eclipse插件ADT遇到错误“requires 'org.ecl
  9. [转]android Android(安卓)SDK Setup的使用及遇到的问题,Faild t

随机推荐

  1. attrs.xml的使用
  2. [android] No resource found that match
  3. Android(安卓)获取系统设置参数。
  4. 用命令行编译APK(英文版)
  5. Android(安卓)Studio 3.5.1 在项目的buil
  6. android intent 传递各种结构数据
  7. 【转】android好文章或博客地址收藏
  8. Android(安卓)图片阴影处理分析!
  9. Android(安卓)- Eclipse 基础修改
  10. Android(安卓)notification点击跳转到Act