Created
April 1, 2019 00:24
-
-
Save SirWellington/8d951ab091d52f6f82186924074a42d3 to your computer and use it in GitHub Desktop.
This is the worst function I have ever read, courtesy of Android's FragmentManagerImpl class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void moveToState(Fragment f, int newState, int transit, int transitionStyle, | |
boolean keepActive) { | |
// Fragments that are not currently added will sit in the onCreate() state. | |
if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) { | |
newState = Fragment.CREATED; | |
} | |
if (f.mRemoving && newState > f.mState) { | |
if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) { | |
// Allow the fragment to be created so that it can be saved later. | |
newState = Fragment.CREATED; | |
} else { | |
// While removing a fragment, we can't change it to a higher state. | |
newState = f.mState; | |
} | |
} | |
// Defer start if requested; don't allow it to move to STARTED or higher | |
// if it's not already started. | |
if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.ACTIVITY_CREATED) { | |
newState = Fragment.ACTIVITY_CREATED; | |
} | |
if (f.mState <= newState) { | |
// For fragments that are created from a layout, when restoring from | |
// state we don't want to allow them to be created until they are | |
// being reloaded from the layout. | |
if (f.mFromLayout && !f.mInLayout) { | |
return; | |
} | |
if (f.getAnimatingAway() != null || f.getAnimator() != null) { | |
// The fragment is currently being animated... but! Now we | |
// want to move our state back up. Give up on waiting for the | |
// animation, move to whatever the final state should be once | |
// the animation is done, and then we can proceed from there. | |
f.setAnimatingAway(null); | |
f.setAnimator(null); | |
moveToState(f, f.getStateAfterAnimating(), 0, 0, true); | |
} | |
switch (f.mState) { | |
case Fragment.INITIALIZING: | |
if (newState > Fragment.INITIALIZING) { | |
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); | |
if (f.mSavedFragmentState != null) { | |
f.mSavedFragmentState.setClassLoader(mHost.getContext() | |
.getClassLoader()); | |
f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( | |
FragmentManagerImpl.VIEW_STATE_TAG); | |
Fragment target = getFragment(f.mSavedFragmentState, | |
FragmentManagerImpl.TARGET_STATE_TAG); | |
f.mTargetWho = target != null ? target.mWho : null; | |
if (f.mTargetWho != null) { | |
f.mTargetRequestCode = f.mSavedFragmentState.getInt( | |
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); | |
} | |
if (f.mSavedUserVisibleHint != null) { | |
f.mUserVisibleHint = f.mSavedUserVisibleHint; | |
f.mSavedUserVisibleHint = null; | |
} else { | |
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean( | |
FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true); | |
} | |
if (!f.mUserVisibleHint) { | |
f.mDeferStart = true; | |
if (newState > Fragment.ACTIVITY_CREATED) { | |
newState = Fragment.ACTIVITY_CREATED; | |
} | |
} | |
} | |
f.mHost = mHost; | |
f.mParentFragment = mParent; | |
f.mFragmentManager = mParent != null | |
? mParent.mChildFragmentManager : mHost.mFragmentManager; | |
// If we have a target fragment, push it along to at least CREATED | |
// so that this one can rely on it as an initialized dependency. | |
if (f.mTarget != null) { | |
if (mActive.get(f.mTarget.mWho) != f.mTarget) { | |
throw new IllegalStateException("Fragment " + f | |
+ " declared target fragment " + f.mTarget | |
+ " that does not belong to this FragmentManager!"); | |
} | |
if (f.mTarget.mState < Fragment.CREATED) { | |
moveToState(f.mTarget, Fragment.CREATED, 0, 0, true); | |
} | |
f.mTargetWho = f.mTarget.mWho; | |
f.mTarget = null; | |
} | |
if (f.mTargetWho != null) { | |
Fragment target = mActive.get(f.mTargetWho); | |
if (target == null) { | |
throw new IllegalStateException("Fragment " + f | |
+ " declared target fragment " + f.mTargetWho | |
+ " that does not belong to this FragmentManager!"); | |
} | |
if (target.mState < Fragment.CREATED) { | |
moveToState(target, Fragment.CREATED, 0, 0, true); | |
} | |
} | |
dispatchOnFragmentPreAttached(f, mHost.getContext(), false); | |
f.mCalled = false; | |
f.onAttach(mHost.getContext()); | |
if (!f.mCalled) { | |
throw new SuperNotCalledException("Fragment " + f | |
+ " did not call through to super.onAttach()"); | |
} | |
if (f.mParentFragment == null) { | |
mHost.onAttachFragment(f); | |
} else { | |
f.mParentFragment.onAttachFragment(f); | |
} | |
dispatchOnFragmentAttached(f, mHost.getContext(), false); | |
if (!f.mIsCreated) { | |
dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false); | |
f.performCreate(f.mSavedFragmentState); | |
dispatchOnFragmentCreated(f, f.mSavedFragmentState, false); | |
} else { | |
f.restoreChildFragmentState(f.mSavedFragmentState); | |
f.mState = Fragment.CREATED; | |
} | |
} | |
// fall through | |
case Fragment.CREATED: | |
// This is outside the if statement below on purpose; we want this to run | |
// even if we do a moveToState from CREATED => *, CREATED => CREATED, and | |
// * => CREATED as part of the case fallthrough above. | |
ensureInflatedFragmentView(f); | |
if (newState > Fragment.CREATED) { | |
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); | |
if (!f.mFromLayout) { | |
ViewGroup container = null; | |
if (f.mContainerId != 0) { | |
if (f.mContainerId == View.NO_ID) { | |
throwException(new IllegalArgumentException( | |
"Cannot create fragment " | |
+ f | |
+ " for a container view with no id")); | |
} | |
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId); | |
if (container == null && !f.mRestored) { | |
String resName; | |
try { | |
resName = f.getResources().getResourceName(f.mContainerId); | |
} catch (Resources.NotFoundException e) { | |
resName = "unknown"; | |
} | |
throwException(new IllegalArgumentException( | |
"No view found for id 0x" | |
+ Integer.toHexString(f.mContainerId) + " (" | |
+ resName | |
+ ") for fragment " + f)); | |
} | |
} | |
f.mContainer = container; | |
f.performCreateView(f.performGetLayoutInflater( | |
f.mSavedFragmentState), container, f.mSavedFragmentState); | |
if (f.mView != null) { | |
f.mInnerView = f.mView; | |
f.mView.setSaveFromParentEnabled(false); | |
if (container != null) { | |
container.addView(f.mView); | |
} | |
if (f.mHidden) { | |
f.mView.setVisibility(View.GONE); | |
} | |
f.onViewCreated(f.mView, f.mSavedFragmentState); | |
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, | |
false); | |
// Only animate the view if it is visible. This is done after | |
// dispatchOnFragmentViewCreated in case visibility is changed | |
f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE) | |
&& f.mContainer != null; | |
} else { | |
f.mInnerView = null; | |
} | |
} | |
f.performActivityCreated(f.mSavedFragmentState); | |
dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false); | |
if (f.mView != null) { | |
f.restoreViewState(f.mSavedFragmentState); | |
} | |
f.mSavedFragmentState = null; | |
} | |
// fall through | |
case Fragment.ACTIVITY_CREATED: | |
if (newState > Fragment.ACTIVITY_CREATED) { | |
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); | |
f.performStart(); | |
dispatchOnFragmentStarted(f, false); | |
} | |
// fall through | |
case Fragment.STARTED: | |
if (newState > Fragment.STARTED) { | |
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); | |
f.performResume(); | |
dispatchOnFragmentResumed(f, false); | |
f.mSavedFragmentState = null; | |
f.mSavedViewState = null; | |
} | |
} | |
} else if (f.mState > newState) { | |
switch (f.mState) { | |
case Fragment.RESUMED: | |
if (newState < Fragment.RESUMED) { | |
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); | |
f.performPause(); | |
dispatchOnFragmentPaused(f, false); | |
} | |
// fall through | |
case Fragment.STARTED: | |
if (newState < Fragment.STARTED) { | |
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); | |
f.performStop(); | |
dispatchOnFragmentStopped(f, false); | |
} | |
// fall through | |
case Fragment.ACTIVITY_CREATED: | |
if (newState < Fragment.ACTIVITY_CREATED) { | |
if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f); | |
if (f.mView != null) { | |
// Need to save the current view state if not | |
// done already. | |
if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { | |
saveFragmentViewState(f); | |
} | |
} | |
f.performDestroyView(); | |
dispatchOnFragmentViewDestroyed(f, false); | |
if (f.mView != null && f.mContainer != null) { | |
// Stop any current animations: | |
f.mContainer.endViewTransition(f.mView); | |
f.mView.clearAnimation(); | |
AnimationOrAnimator anim = null; | |
if (mCurState > Fragment.INITIALIZING && !mDestroyed | |
&& f.mView.getVisibility() == View.VISIBLE | |
&& f.mPostponedAlpha >= 0) { | |
anim = loadAnimation(f, transit, false, | |
transitionStyle); | |
} | |
f.mPostponedAlpha = 0; | |
if (anim != null) { | |
animateRemoveFragment(f, anim, newState); | |
} | |
f.mContainer.removeView(f.mView); | |
} | |
f.mContainer = null; | |
f.mView = null; | |
// Set here to ensure that Observers are called after | |
// the Fragment's view is set to null | |
f.mViewLifecycleOwner = null; | |
f.mViewLifecycleOwnerLiveData.setValue(null); | |
f.mInnerView = null; | |
f.mInLayout = false; | |
} | |
// fall through | |
case Fragment.CREATED: | |
if (newState < Fragment.CREATED) { | |
if (mDestroyed) { | |
// The fragment's containing activity is | |
// being destroyed, but this fragment is | |
// currently animating away. Stop the | |
// animation right now -- it is not needed, | |
// and we can't wait any more on destroying | |
// the fragment. | |
if (f.getAnimatingAway() != null) { | |
View v = f.getAnimatingAway(); | |
f.setAnimatingAway(null); | |
v.clearAnimation(); | |
} else if (f.getAnimator() != null) { | |
Animator animator = f.getAnimator(); | |
f.setAnimator(null); | |
animator.cancel(); | |
} | |
} | |
if (f.getAnimatingAway() != null || f.getAnimator() != null) { | |
// We are waiting for the fragment's view to finish | |
// animating away. Just make a note of the state | |
// the fragment now should move to once the animation | |
// is done. | |
f.setStateAfterAnimating(newState); | |
newState = Fragment.CREATED; | |
} else { | |
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); | |
boolean beingRemoved = f.mRemoving && !f.isInBackStack(); | |
if (beingRemoved || mNonConfig.shouldDestroy(f)) { | |
boolean shouldClear; | |
if (mHost instanceof ViewModelStoreOwner) { | |
shouldClear = mNonConfig.isCleared(); | |
} else if (mHost.getContext() instanceof Activity) { | |
Activity activity = (Activity) mHost.getContext(); | |
shouldClear = !activity.isChangingConfigurations(); | |
} else { | |
shouldClear = true; | |
} | |
if (beingRemoved || shouldClear) { | |
mNonConfig.clearNonConfigState(f); | |
} | |
f.performDestroy(); | |
dispatchOnFragmentDestroyed(f, false); | |
} else { | |
f.mState = Fragment.INITIALIZING; | |
} | |
f.performDetach(); | |
dispatchOnFragmentDetached(f, false); | |
if (!keepActive) { | |
if (beingRemoved || mNonConfig.shouldDestroy(f)) { | |
makeInactive(f); | |
} else { | |
f.mHost = null; | |
f.mParentFragment = null; | |
f.mFragmentManager = null; | |
if (f.mTargetWho != null) { | |
Fragment target = mActive.get(f.mTargetWho); | |
if (target != null && target.getRetainInstance()) { | |
// Only keep references to other retained Fragments | |
// to avoid developers accessing Fragments that | |
// are never coming back | |
f.mTarget = target; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
if (f.mState != newState) { | |
Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; " | |
+ "expected state " + newState + " found " + f.mState); | |
f.mState = newState; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment