Problem of repetition

Recently, a very strange problem appeared, the problem abnormal log is as follows:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:2053)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:2079)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:678)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:632)
at android.support.v4.app.DialogFragment.dismissInternal(DialogFragment.java:223)
at android.support.v4.app.DialogFragment.dismiss(DialogFragment.java:190)
Copy the code

DialogFragment calls show and does not check whether the Activity has finished. It turns out that there is a judgment in the project code, and it must not be so simple.

FragmentManagerImpl.checkStateLoss

private void checkStateLoss() {// Where the exception is thrownif (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); }}Copy the code

FragmentManagerImpl. enqueueAction

/**
 * Adds an action to the queue of pending actions.
 *
 * @param action the action to add
 * @param allowStateLoss whether to allow loss of state information
 * @throws IllegalStateException ifthe activity has been destroyed */ public void enqueueAction(OpGenerator action, Boolean allowStateLoss) {// Notice the allowStateLoss here, which determines whether the checkStateLoss method is performedif(! allowStateLoss) { checkStateLoss(); } synchronized (this) {if (mDestroyed || mHost == null) {
            throw new IllegalStateException("Activity has been destroyed");
        }
        if(mPendingActions == null) { mPendingActions = new ArrayList<>(); } mPendingActions.add(action); scheduleCommit(); }}Copy the code

BackStackRecord.commitInternal

int commitInternal(boolean allowStateLoss) {
    if (mCommitted) throw new IllegalStateException("commit already called");
    if (FragmentManagerImpl.DEBUG) {
        Log.v(TAG, "Commit: " + this);
        LogWriter logw = new LogWriter(TAG);
        PrintWriter pw = new PrintWriter(logw);
        dump("", null, pw, null);
        pw.close();
    }
    mCommitted = true;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else{ mIndex = -1; } // focus mmanager.enqueueAction (this, allowStateLoss);return mIndex;
}
Copy the code

BackStackRecord.commit

public int commit() {// The allowStateLoss argument is passed inreturn commitInternal(false);
}
Copy the code

DialogFragment.show

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true; FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); // Show calls commit(); ft.com MIT (); }Copy the code

The Fragment manager flag mStateSaved = true is used because the DialogFragment show method is used to forcibly check the state of the Fragment and the state is already saved at the time of the check.

Scene reappearance

OnSaveInstanceState is triggered when the user presses the Home button when entering the Home page module or when the user runs out of memory, causing the FragmentManager to “intelligently” save the Fragment state of the current Activity. The DialogFragment (AD pop-up) needs to pull data from the server first, so it raises an exception when checkStateLoss is checked during show.

The solution

  • 1. Change the AD pop-up implementation to pureDialogNo longer in useDialogFragment
  • 2. If your own project does not need toFragmentThe relevant state can be saved and maintained in the relevantActivityIn facsimileonSaveInstanceDon’t makesuperCall back (This scenario is not recommended);
  • 3, can be rewrittenDialogFragmentIn theshowMethod, and usecommitAllowStateLossSubmit with the following code (This solution is recommended).
Override fun show(manager: FragmentManager? , tag: String?) { // super.show(manager, tag) val ft = manager? .beginTransaction() ft? .add(this, tag) ft? .commitAllowingStateLoss() }Copy the code