[android] “IllegalStateException : onSaveInstanceState 후에이 작업을 수행 할 수 없습니다.”예외가 발생 함

라이브 Android 응용 프로그램이 있으며 시장에서 다음과 같은 스택 추적을 받았으며 응용 프로그램 코드에서 발생하지 않지만 응용 프로그램에서 일부 또는 다른 이벤트로 인해 발생하는 이유를 모릅니다 (가정)

Fragments를 사용하고 있지 않지만 여전히 FragmentManager에 대한 참조가 있습니다. 이런 유형의 문제를 피하기 위해 어떤 몸이 숨겨진 사실에 빛을 비출 수 있다면 :

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  



답변

이것은 내가 지금까지 만난 가장 바보 같은 버그입니다. API <11API> 11 에서 Fragment완벽하게 작동 하는 응용 프로그램이 있습니다.Force Closing

Activity에 대한 호출 에서 수명주기 내에서 무엇이 바뀌 었는지 알 수 saveInstance없었지만 여기에이를 해결하는 방법이 있습니다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

나는 단지 전화하지 않고 .super()모든 것이 훌륭하게 작동합니다. 시간이 절약 되길 바랍니다.

편집 : 좀 더 연구 한 후에 이것은 지원 패키지 의 알려진 버그 입니다.

인스턴스를 저장하고 무언가를 추가 해야하는 경우 outState Bundle다음을 사용할 수 있습니다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDIT2 :Activity 백그라운드에서 거래를 마치고 거래를 시도하는 경우에도 발생할 수 있습니다 . 이것을 피하기 위해 사용해야합니다commitAllowingStateLoss()

EDIT3 : 위의 솔루션은 내가 기억할 수있는 초기 support.v4 라이브러리의 문제를 수정했습니다. 당신은 여전히이 문제를 가지고있는 경우 그러나 당신은 반드시 도 읽을 @AlexLockwood ‘의 블로그 조각 거래 및 활동 상태의 손실을

블로그 게시물의 요약 (그러나 나는 그것을 읽는 것이 좋습니다) :

  • NEVER의 commit() 후 거래를 onPause()사전에 벌집에, 그리고 onStop()이후 벌집에
  • Activity라이프 사이클 메소드 내에서 트랜잭션을 커미트 할 때주의하십시오 . 사용 onCreate() , onResumeFragments()onPostResume()
  • 비동기 콜백 메소드 내에서 트랜잭션을 수행하지 마십시오
  • commitAllowingStateLoss()최후의 수단으로 만 사용

답변

이 문제의 원인에 대한 Android 소스 코드를 살펴보면 FragmentManagerImpl클래스 에서 mStateSaved 플래그 (Activity에서 사용 가능한 인스턴스)의 값이 true입니다. 호출시 백 스택이 저장 될 때 (saveAllState) true로 설정됩니다 Activity#onSaveInstanceState. 이후 ActivityThread의 호출은 FragmentManagerImpl#noteStateNotSaved()및 에서 사용 가능한 재설정 메소드를 사용하여이 플래그를 재설정하지 않습니다 dispatch().

내가 보는 방식에는 앱이 수행하고 사용하는 작업에 따라 사용 가능한 수정 사항이 있습니다.

좋은 방법

다른 무엇보다도 : 나는 Alex Lockwood 기사를 광고 할 것 입니다. 그런 다음 내가 지금까지 한 일에서 :

  1. 상태 정보를 유지할 필요가없는 프래그먼트 및 활동의 경우 commitAllowStateLoss를 호출 하십시오 . 문서에서 가져온 것 :

    활동 상태가 저장된 후 커밋을 실행할 수 있습니다. 활동을 나중에 상태에서 복원해야 할 경우 커밋이 유실 될 수 있으므로 위험합니다. 따라서 UI 상태가 사용자에게 예기치 않게 변경 될 수있는 경우에만 사용해야합니다. 조각이 읽기 전용 정보를 표시하는 경우 이것이 사용하는 것이 좋습니다. 또는 편집 가능한 정보를 표시하더라도 콜백 메소드를 사용하여 편집 된 정보를 유지하십시오.

  2. 트랜잭션이 커밋 된 직후 (방금 전화 commit())을 (를 ) 호출합니다 FragmentManager.executePendingTransactions().

권장되지 않는 방법 :

  1. 위에서 언급 한 Ovidiu Latcu는 전화하지 마십시오 super.onSaveInstanceState(). 그러나 이것은 조각 상태와 함께 활동의 전체 상태를 잃을 것임을 의미합니다.

  2. 재정의 onBackPressed하고 거기에서만 호출합니다 finish(). 응용 프로그램에서 Fragments API를 사용하지 않으면 괜찮습니다. 에 super.onBackPressed대한 호출이 FragmentManager#popBackStackImmediate()있습니다.

  3. Fragments API를 모두 사용하고 있고 활동 상태가 중요 / 중요한 경우 리플렉션 API를 사용하여 호출 할 수 FragmentManagerImpl#noteStateNotSaved()있습니다. 그러나 이것은 해킹이거나 해결 방법이라고 말할 수 있습니다. 나는 그것을 좋아하지 않지만, 더 이상 사용되지 않는 코드를 사용하는 레거시 응용 프로그램의 코드 ( TabActivity암시 적으로 LocalActivityManager) 가 있기 때문에 꽤 괜찮습니다 .

다음은 리플렉션을 사용하는 코드입니다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

건배!


답변

프래그먼트 활동 onSaveInstanceState()이 호출 된 후 프래그먼트 전환을 수행하려고하면 이러한 예외가 발생합니다 .

이러한 일이 발생할 수있는 한 가지 이유 는 활동이 중지 될 때 AsyncTask(또는 Thread)을 실행 상태로 두는 것 입니다.

onSaveInstanceState()시스템이 자원에 대한 활동을 재 확보하고 나중에 다시 작성하면 호출 후 전환 이 잠재적으로 손실 될 수 있습니다.


답변

간단히 전화 super.onPostResume ()을 당신의 조각을 표시하기 전에 또는 super.onPostResume를 호출 한 후) (onPostResume에 방법을 코드를 이동 (). 이것은 문제를 해결합니다!


답변

dismiss()화면이 잠기거나 비워지고 Activity + 대화 상자의 인스턴스 상태가 저장된 후 대화 상자 조각을 호출 할 때도 발생할 수 있습니다 . 이 전화를 피하려면 :

dismissAllowingStateLoss()

문자 그대로 대화 상자를 닫을 때마다 더 이상 상태에 대해 신경 쓰지 않으므로 그렇게해도 괜찮습니다. 실제로 상태를 잃지 않습니다.


답변

짧고 효과적인 해결책 :

간단한 단계를 따르십시오 :

1 단계 : 각 조각에서 onSaveInstanceState 상태를 재정의합니다. 그리고 슈퍼 메소드를 제거하십시오.

@Override
public void onSaveInstanceState(Bundle outState) {
};

2 단계 : CommitAllowingStateLoss () 사용 commit () 대신; 조각 작업 중.

fragmentTransaction.commitAllowingStateLoss();


답변

라이프 사이클 상태는 Android 지원 lib v26.1.0에서 시작하여 이러한 충돌을 방지하는 데 도움이 될 수 있다고 생각합니다.

if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)){
  // Do fragment's transaction commit
}

또는 당신은 시도 할 수 있습니다 :

Fragment.isStateSaved()

자세한 내용은 여기
https://developer.android.com/reference/android/support/v4/app/Fragment.html#isStateSaved ()