Android Bugs——DialogFragment.showNow:Can not perform this action after onSaveInstanceState

problem analysis:

1. When to call the onSaveInstanceState() method

onSaveInstanceState()Called to save the state of each instance before the Activity is destroyed, so that the state can be guaranteed to be recovered from onCreate(Bundle)or onRestoreInstanceState(Bundle)restored to.

The onSaveInstanceState method of the activity will call FragmentManager#saveAllStatethe method to save the state of the Fragment, which is used 同时设置mStateSaved为trueto indicate that the state has been saved.

2. When will Can not perform this action after onSaveInstanceState appear?

There are two situations where the Can not perform this action after onSaveInstanceState exception occurs:

1. FragmentTransaction的commit()时出现:
2. Activity/FragmentActivity的onBackPressed时出现:

commitIn both cases the or etc. methods are called after the state is stored onBackPressed, which will call checkStateLoss(); throwing an exception.

private void checkStateLoss() {
    
    
	if (isStateSaved()) {
    
    
		throw new IllegalStateException("Can not perform this action after  onSaveInstanceState");
	}
}

@Override
public boolean isStateSaved() {
    
    
	// See saveAllState() for the explanation of this.  We do this for
    // all platform versions, to keep our behavior more consistent between
    // them.
    return mStateSaved || mStopped;
}

Detailed reference: https://www.jianshu.com/p/ee58be253a5b
Okay, let’s go back to the place where we reported the error DialogFragment.showNow, first look at the source code:

public void showNow(@NonNull FragmentManager manager, @Nullable String tag) {
    
    
	mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commitNow();
}

From the above code, it can be seen that a Fragment transaction submission has been made, and the commitNow method is used. Continue to find the source code:

@Override
public void commitNow() {
    
    
	disallowAddToBackStack();
	mManager.execSingleAction(this, false);
}

Here we see execSingleAction的第二个参数allowStateLoss传值fasle,

private void ensureExecReady(boolean allowStateLoss) {
    
    
	...

    if (!allowStateLoss) {
    
    
    	checkStateLoss();
    }

    if (mTmpRecords == null) {
    
    
    	mTmpRecords = new ArrayList<>();
        mTmpIsPop = new ArrayList<>();
    }
    mExecutingActions = true;
    try {
    
    
    	executePostponedTransaction(null, null);
    } finally {
    
    
    	mExecutingActions = false;
    }
}

It will eventually execSingleActionbe executed ensureExecReady, ensureExecReady同样进行了checkStateLoss()状态检查, so the reason for the error is self-evident, so let's talk about the solution below.

solution:

We all know that Fragment's transaction submission has two methods commit()and commitAllowingStateLoss()if we use it commitAllowingStateLoss()就会避免上述报错. So can the show method of DialogFragment be modified according to this idea? The answer is yes. Go directly to the code.

@Override
public void showNow(@NonNull FragmentManager manager, @Nullable String tag) {
    
    
	try {
    
    
    	//由于父类方法中mDismissed,mShownByMe不可直接访问,所以此处采用反射修改他们的值
        Class dialogFragmentClass = DialogFragment.class;
        Field mDismissed = dialogFragmentClass.getDeclaredField("mDismissed");
        mDismissed.setAccessible(true);
        mDismissed.set(this, false);

        Field mShownByMe = dialogFragmentClass.getDeclaredField("mShownByMe");
        mShownByMe.setAccessible(true);
        mShownByMe.set(this, true);

        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commitNowAllowingStateLoss();
    } catch (Exception e)  {
    
    
        e.printStackTrace();
    }
}

Guess you like

Origin blog.csdn.net/u012230055/article/details/129573315