Background
Asynchronous Callbacks in Android
Trying to perform an asynchronous operation in a reliable fashion on Android is unnecessarily convoluted i.e. Is AsyncTask really conceptually flawed or am I just missing something?
Now, this is all prior to the introduction of Fragments. With the introduction of Fragments, onRetainNonConfigurationInstance() 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.)
Example: https://code.google.com/p/android/issues/detail?id=23096#c4
IllegalStateException - Can not perform this action after onSaveInstanceState
Theoretically the hack above allows you to get around the dreaded:
IllegalStateException - "Can not perform this action after onSaveInstanceState"
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.
mStateSaved being the variable who is ultimately responsible for firing this exception.
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);
}
}
So theoretically, now you know when it's safe to perform fragment transactions, and you can hence avoid the dreaded IllegalStateException.
Wrong!
Nested Fragments
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.
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?
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
Anyway, getting a bit off topic.
Dead-end solution
So, you might think the solution to the problem is simply, which seems like a bit of an antonym at this point, 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.
Wrong again!
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.
My Question
Does someone out there have a solution for determining when it is safe to perform fragment transactions on child fragments in conjunction with asynchronous code callbacks?