Note that there are some explanatory texts on larger screens.

plurals
  1. PONested Fragments - IllegalStateException "Can not perform this action after onSaveInstanceState"
    text
    copied!<h1>Background</h1> <h3>Asynchronous Callbacks in Android</h3> <p>Trying to perform an asynchronous operation in a reliable fashion on Android is unnecessarily convoluted i.e. <a href="https://stackoverflow.com/questions/3357477/is-asynctask-really-conceptually-flawed-or-am-i-just-missing-something">Is AsyncTask really conceptually flawed or am I just missing something?</a></p> <p>Now, this is all prior to the introduction of Fragments. With the introduction of Fragments, <a href="http://developer.android.com/reference/android/app/Activity.html#onRetainNonConfigurationInstance%28%29" rel="nofollow noreferrer">onRetainNonConfigurationInstance()</a> has been deprecated. So the latest Google condoned hack is to use a persistent non-UI fragment that attaches/detaches from your Activity when configuration changes occur (i.e. Rotating the screen, changing language settings etc.)</p> <p>Example: <a href="https://code.google.com/p/android/issues/detail?id=23096#c4" rel="nofollow noreferrer">https://code.google.com/p/android/issues/detail?id=23096#c4</a></p> <h3>IllegalStateException - Can not perform this action after onSaveInstanceState</h3> <p>Theoretically the hack above allows you to get around the dreaded:</p> <pre><code>IllegalStateException - "Can not perform this action after onSaveInstanceState" </code></pre> <p>because a persistent non-UI fragment will receive callbacks for onViewStateRestored() (alternatively onResume) and onSaveInstanceState() (alternatively onPause). As such you can tell when the instance state is saved/restored. It's a fair bit of code for something so simple, but utilising this knowledge, you could queue up your asynchronous callbacks until the activity's FragmentManager has its mStateSaved variable set to false before executing them.</p> <p>mStateSaved being the variable who is ultimately responsible for firing this exception.</p> <pre><code>private void checkStateLoss() { if (mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } if (mNoTransactionsBecause != null) { throw new IllegalStateException( "Can not perform this action inside of " + mNoTransactionsBecause); } } </code></pre> <p>So theoretically, now you know when it's safe to perform fragment transactions, and you can hence avoid the dreaded IllegalStateException.</p> <p><em>Wrong!</em></p> <h3>Nested Fragments</h3> <p>The solution above only works for the Activity's FragmentManager. Fragments themselves also have a child fragment manager, which is utilised for nesting fragments. Unfortunately, child fragment managers are not kept in sync with the Activity's fragment manager at all. So whilst the activity's fragment manager is up-to-date and always has the correct mStateSaved; child fragments think otherwise and will happily throw the dreaded IllegalStateException at inappropriate times.</p> <p>Now, if you've looked at Fragment.java and FragmentManager.java in the support library you won't in any way be surprised that everything and anything to do with fragments is error prone. The design and code quality is... ah, questionable. Do you like booleans?</p> <pre><code>mHasMenu = false mHidden = false mInLayout = false mIndex = 1 mFromLayout = false mFragmentId = 0 mLoadersStarted = true mMenuVisible = true mNextAnim = 0 mDetached = false mRemoving = false mRestored = false mResumed = true mRetainInstance = true mRetaining = false mDeferStart = false mContainerId = 0 mState = 5 mStateAfterAnimating = 0 mCheckedForLoaderManager = true mCalled = true mTargetIndex = -1 mTargetRequestCode = 0 mUserVisibleHint = true mBackStackNesting = 0 mAdded = true </code></pre> <p>Anyway, getting a bit off topic.</p> <h3>Dead-end solution</h3> <p>So, you might think the solution to the problem is simply, <em>which seems like a bit of an antonym at this point</em>, to add another one of those nice hacky non-UI fragments to your child fragment managers. Presumably its callbacks are in sync with its internal state and things will all be dandy.</p> <p><em>Wrong again!</em></p> <p>Android doesn't support retained fragment instances that are attached as children to other fragments (aka nested fragments). Now, in hindsight this should make sense. If the parent fragment is destroyed when the activity is destroyed because its not retained, there is nowhere to reattach the child fragment. So that's just not going to work.</p> <h1>My Question</h1> <p>Does someone out there have a solution for determining when it is safe to perform fragment transactions <em>on child fragments</em> in conjunction with asynchronous code callbacks? </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload