대화 상자를 표시하면 “onSaveInstanceState 후에이 작업을 수행 할 수 없습니다”라는 메시지가 나타납니다.

일부 사용자는 알림 표시 줄의 빠른 작업을 사용하면 강제 종료됩니다.

“TestDialog” 클래스 를 호출하는 알림에 빠른 작업을 표시합니다 . “snooze”버튼을 누른 후 TestDialog 클래스에서 SnoozeDialog를 표시합니다.

private View.OnClickListener btnSnoozeOnClick() {
    return new View.OnClickListener() {

        public void onClick(View v) {
            showSnoozeDialog();
        }
    };
}

private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    snoozeDialog.show(fm, "snooze_dialog");
}

오류는 *IllegalStateException: Can not perform this action after onSaveInstanceState*.

IllegarStateException이 발생하는 코드 줄은 다음과 같습니다.

snoozeDialog.show(fm, "snooze_dialog");

클래스는 “FragmentActivity”를 확장하고 “SnoozeDialog”클래스는 “DialogFragment”를 확장합니다.

다음은 오류의 전체 스택 추적입니다.

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
at com.test.testing.TestDialog.f(TestDialog.java:538)
at com.test.testing.TestDialog.e(TestDialog.java:524)
at com.test.testing.TestDialog.d(TestDialog.java:519)
at com.test.testing.g.onClick(TestDialog.java:648)
at android.view.View.performClick(View.java:3620)
at android.view.View$PerformClick.run(View.java:14292)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)

이 오류를 재현 할 수 없지만 많은 오류 보고서를 받고 있습니다.

아무도이 오류를 어떻게 해결할 수 있습니까?



답변

이것은 일반적인 문제 입니다. show ()를 재정의하고 DialogFragment 확장 클래스에서 예외를 처리하여이 문제를 해결했습니다.

public class CustomDialogFragment extends DialogFragment {

    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            FragmentTransaction ft = manager.beginTransaction();
            ft.add(this, tag);
            ft.commit();
        } catch (IllegalStateException e) {
            Log.d("ABSDIALOGFRAG", "Exception", e);
        }
    }
}

이 메서드를 적용해도 DialogFragment.class의 내부 필드는 변경되지 않습니다.

boolean mDismissed;
boolean mShownByMe;

이로 인해 경우에 따라 예기치 않은 결과가 발생할 수 있습니다. commit () 대신 commitAllowingStateLoss ()를 더 잘 사용하십시오.


답변

당신을 의미 그 commit()( show()후 조각 DialogFragment의 경우) onSaveInstanceState().

Android는 조각 상태를 onSaveInstanceState(). 따라서 commit()조각 후 onSaveInstanceState()조각화하면 상태가 손실됩니다.

결과적으로 활동이 종료되고 나중에 다시 생성되면 조각이 나쁜 사용자 경험 인 활동에 추가되지 않습니다. 그렇기 때문에 Android는 어떤 대가를 치르더라도 상태 손실을 허용하지 않습니다.

쉬운 해결책은 상태가 이미 저장되었는지 확인하는 것입니다.

boolean mIsStateAlreadySaved = false;
boolean mPendingShowDialog = false;

@Override
public void onResumeFragments(){
    super.onResumeFragments();
    mIsStateAlreadySaved = false;
    if(mPendingShowDialog){
        mPendingShowDialog = false;
        showSnoozeDialog();
    }
}

@Override
public void onPause() {
    super.onPause();
    mIsStateAlreadySaved = true;
}

private void showSnoozeDialog() {
    if(mIsStateAlreadySaved){
        mPendingShowDialog = true;
    }else{
        FragmentManager fm = getSupportFragmentManager();
        SnoozeDialog snoozeDialog = new SnoozeDialog();
        snoozeDialog.show(fm, "snooze_dialog");
    }
}

참고 : 조각이 재개되면 onResumeFragments ()가 호출됩니다.


답변

private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    // snoozeDialog.show(fm, "snooze_dialog");
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(snoozeDialog, "snooze_dialog");
    ft.commitAllowingStateLoss();
}

참조 : 링크


답변

며칠 후 내 솔루션을 어떻게 수정했는지 공유하고 DialogFragment를 표시하려면 show()메서드 를 재정의 commitAllowingStateLoss()하고 Transaction개체를 호출 해야 합니다. 다음은 Kotlin의 예입니다.

override fun show(manager: FragmentManager?, tag: String?) {
        try {
            val ft = manager?.beginTransaction()
            ft?.add(this, tag)
            ft?.commitAllowingStateLoss()
        } catch (ignored: IllegalStateException) {

        }

    }

답변

대화 상자가 실제로 중요하지 않은 경우 (앱이 닫히거나 더 이상 표시되지 않을 때 표시하지 않아도 괜찮음) 다음을 사용하십시오.

boolean running = false;

@Override
public void onStart() {
    running = true;
    super.onStart();
}

@Override
public void onStop() {
    running = false;
    super.onStop();
}

그리고 실행 중일 때만 대화 상자 (조각)를 엽니 다.

if (running) {
    yourDialog.show(...);
}

아마도 더 나은 솔루션 편집 :

수명주기에서 onSaveInstanceState가 호출되는 경우 예측할 수없는 경우 다음과 같이 isSavedInstanceStateDone ()을 확인하는 것이 더 나은 해결책이라고 생각합니다.

/**
 * True if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
private boolean savedInstanceStateDone;

@Override
protected void onResume() {
    super.onResume();

    savedInstanceStateDone = false;
}

@Override
protected void onStart() {
    super.onStart();

    savedInstanceStateDone = false;
}

protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    savedInstanceStateDone = true;
}


/**
 * Returns true if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
public boolean isSavedInstanceStateDone() {
    return savedInstanceStateDone;
}

답변

나는이 문제에 수년간 뛰어 들었다.
인터넷은 이것에 대한 토론의 수 (수백? 수천?)로 흩어져 있으며, 그 안에있는 혼란과 허위 정보가 많은 것 같습니다.
상황을 더 악화시키기 위해, xkcd “14 표준”만화의 정신으로, 나는 반지에 대한 나의 대답을 던지고 있습니다.

cancelPendingInputEvents(), commitAllowingStateLoss(), catch (IllegalStateException e), 유사한 솔루션과 모든 극악한 보인다.

다음은 문제를 재현하고 수정하는 방법을 쉽게 보여줍니다.

private static final Handler sHandler = new Handler();
private boolean mIsAfterOnSaveInstanceState = true;

@Override
protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    mIsAfterOnSaveInstanceState = true; // <- To repro, comment out this line
}

@Override
protected void onPostResume()
{
    super.onPostResume();
    mIsAfterOnSaveInstanceState = false;
}

@Override
protected void onResume()
{
    super.onResume();
    sHandler.removeCallbacks(test);
}

@Override
protected void onPause()
{
    super.onPause();
    sHandler.postDelayed(test, 5000);
}

Runnable test = new Runnable()
{
    @Override
    public void run()
    {
        if (mIsAfterOnSaveInstanceState)
        {
            // TODO: Consider saving state so that during or after onPostResume a dialog can be shown with the latest text
            return;
        }

        FragmentManager fm = getSupportFragmentManager();
        DialogFragment dialogFragment = (DialogFragment) fm.findFragmentByTag("foo");
        if (dialogFragment != null)
        {
            dialogFragment.dismiss();
        }

        dialogFragment = GenericPromptSingleButtonDialogFragment.newInstance("title", "message", "button");
        dialogFragment.show(fm, "foo");

        sHandler.postDelayed(test, 5000);
    }
};

답변

FragmentManager 대신 FragmentTransaction을 사용하십시오. 아래 코드가 문제를 해결할 것이라고 생각합니다. 그렇지 않은 경우 알려주십시오.

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(ft, "snooze_dialog");

편집하다:

조각 거래

이 링크를 확인하십시오. 나는 그것이 당신의 질문을 해결할 것이라고 생각합니다.