"IllegalStateException : onSaveInstanceState 후에이 작업을 수행 할 수 없습니다."예외가 발생 함


355

라이브 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)  

아직 해결책을 찾았습니까? 여기에 같은 문제가 : stackoverflow.com/questions/7575921/...
nhaarman

1
나는 같은 문제가 있었고 나에게 맞는 간단한 해결책을 찾았다
phlebas

2
@phlebas 아뇨. 당신은 대화와 관련이 있지만, 그렇지 않습니다. 스택 추적 일치의 최상위 줄이 충분하지 않습니다. 나머지는 매우 다릅니다. 나는 당신의 문제를 방금 조사했기 때문에 이것을 말하지만 불행히도 나에게는 도움이되지 않습니다.
themightyjon

해당 활동에 스레드 또는 AsynTask를 사용합니까?
José Castro

21
블로그 게시물 에서이 오류에 대해 토론합니다 ... 읽어야합니다. :)
Alex Lockwood 2016 년

답변:


455

이것은 내가 지금까지 만난 가장 바보 같은 버그입니다. 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()최후의 수단으로 만 사용

97
commit () 대신 commitAllowingStateLoss ()를 사용해야합니다.
meh

7
따라서 onSaveInstanceState에서 super를 호출하지 않으면 FragmentManager가 모든 조각의 상태를 저장하고 복원 할 수 없게됩니다. 회전 문제가 발생할 수 있습니다. 또한 방금 정크를 번들에 넣는 것에 대해 다른 것을 시도했지만 아무런 차이가 없습니다. 지원 패키지에서 참조한 버그는 NullPointerException이며,이 IllegalStateException처럼 보이지는 않습니다.
themightyjon

56
@meh commitAllowingStateLoss()는 예외를 피합니다. 우발적 인 상태 손실로부터 응용 프로그램을 보호하지는 않습니다. 이 블로그 게시물을 참조하십시오 .
Alex Lockwood

2
@AlexLockwood 그래서 블로그 게시물에서 우리는 프래그먼트 내부에서 모든 네트워크 호출을 수행하고 필요한 경우 임시 진행률을 표시해야 함을 알 수 있습니다. 일부 비동기 메소드 호출 후에 호출됩니다.
meh

1
첫 번째 요점은 얻지 못했습니다 : "post-Honeycomb에서 ... onStop () 후 commit () 트랜잭션을 절대로 수행하지 마십시오". 프래그먼트를 다른 프래그먼트로 교체하기 위해 버튼이 필요한 경우 어떻게해야합니까? 활동이 onStop에서 완료되었는지 확인하는 부울을 넣어야합니까? 그렇지 않으면 commitAllowingStateLoss를 대신 호출합니까? 또한 조각 내에 조각이 있으면 버튼을 클릭하면 교체해야합니까?
안드로이드 개발자

76

이 문제의 원인에 대한 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);
    }
}

건배!


이것은 진저 브레드 아래 ActionBarSherlock에서도 발생하는 것으로
보이므로

또한, 당신은 지적해야합니다-그것은 ABS 사용에도 적합하지 않습니다 :)
t0mm13b

@ t0mm13b : 위의 코드는 프래그먼트를 사용하지 않거나 지원하지 않기 때문에 내 프로젝트를 나타냅니다 .FragmentActivity. android.app.Activity에서 실행되며 예외를 유발하는 불일치가 FragmentManager (API 레벨 11 이상)에 의해 발생하기 때문에 확인 이유입니다 ... 악의 근원이 동일하다고 생각하면 자유롭게 느끼십시오. 수표를 제거하십시오. ABS는 호환성 패키지와 지원 구현을 기반으로 실행되는 이야기가 다릅니다 .FragmentActivity는 FragmentManager와 voila의 동일한 구현 : 동일한 문제를 사용할 수 있습니다.
gunar

@ t0mm13b : 600 자로 충분하지 않기 때문에 더 많이 추가하려면 실제로이 문제를 일으키는 원인을 먼저 조사해야합니다. 당신은 또한 위의 추악한 해킹임을 알고 있어야하며 그것이 실행되는지 여부에 대해 책임을지지 않습니다 (나에게는 환경을 고려한 최고의 솔루션이었습니다). 사용해야하는 경우, 표준 패키지와 다를 수 있으므로 변수 이름에 대해 compat 소스 코드를 다시 확인하십시오. 이 문제가 다음 버전의 호환성 패키지에 의해 해결되기를 바랍니다. 그러나 Android 환경에서는 발생할 가능성이 거의 없습니다 ...
gunar

어 ..이 버그 보고서 와 정확히 같은 문제 인데 ,이 OP의 질문의 요점입니다. 나는 내 댓글 옆에 서 - 당신이 해야 명시 적으로 부인에 넣어 가지고 그것이 보장되지 않으므로 말하고도 해야 당신이 조각을 사용하지 않았다고 진술 한 - 그렇지 않으면 왜 그 중 하나 대답을 게시 귀찮게! :) 그냥 ...
t0mm13b 21:50에

35

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

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

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


2
Funk, 여기에 질문이 있습니다. 해당 활동 또는 프래그먼트가 중지 된 경우 어떻게 해당 활동 또는 프래그먼트에서 onBackPressed를 호출 할 수 있습니까? 위의 예외는 일부 UI 이벤트 (예 : BACK 키 누름)에서 생성 된 것으로 보이지만 Async Task와 Back 키 사이의 관계를 찾을 수 없습니다.
dcool

조각 전환을 뒤로 상태로 저장할 수 있으므로 뒤로 버튼을 누르면 저장 한 전환이 반대로되어 오래된 조각이 다시 나타날 수 있습니다. onStop이 호출 된 후가 아니라 항상 시스템에 자원을 복원하기 위해 활동이 파괴되기 전에 onSaveInstanceState가 호출됩니다. 죄송합니다. 대답이 명확하지 않았습니다.
FunkTheMonk

펑크,하지만 내 응용 프로그램에서 조각을 사용하지 않습니다. 네이티브 코드에서 사용 된 조각 일 수 있습니다. 이전에 나는 당신이 같은 것에 대해 이야기하고 있다고 생각했습니다.
dcool

1
조각에 대한 참조가있는 AsyncTask가 있습니다. onSaveInstanceState에서 super () 호출을 제거하고 내 AsyncTask의 참조를 WeakReference <Fragment>로 바꾼 후 문제가 해결되었습니다.
버팔로

2
@Buffalo 그것은 문제에 대한 해결책이 아닙니다. 항상 전화해야합니다 super.onSaveInstanceState().
Alex Lockwood

27

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


6
onPostResume ()을 호출하면 onResumeFragments ()가 호출되고 이것이 나에게 이상적인 솔루션입니다.
j2emanue

20

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

dismissAllowingStateLoss()

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


2
이것은 내 정확한 문제였습니다! 당신은 훌륭합니다!
Tash Pemhiwa

17

짧고 효과적인 해결책 :

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

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

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

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

fragmentTransaction.commitAllowingStateLoss();

1
슈퍼 메소드를 제거하면 트릭이 발생했습니다. 왜 그런지 설명 할 수 있습니까? 그것을 제거하는 것이 안전합니까?
Bruce

7
super ()를 제거하는 것이 안전하지 않으므로 그 후에 다른 데이터 conf가 손실됩니다!
deadfish


7

이것은 나를 위해 일했다 ... 내가 이것을 발견 ... 도움이되기를 바랍니다!

1) 글로벌 "정적"FragmentManager / FragmentTransaction이 없습니다.

2) onCreate, 항상 FragmentManager를 다시 초기화하십시오!

아래 샘플 :-

public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedInstanceState = savedInstanceState;
    setDefaultFragments();
}

protected void setDefaultFragments() {
    fragmentManager = getSupportFragmentManager();
    //check if on orientation change.. do not re-add fragments!
    if(mSavedInstanceState == null) {
        //instantiate the fragment manager

        fragmentTransaction = fragmentManager.beginTransaction();

        //the navigation fragments
        NavigationFragment navFrag = new NavigationFragment();
        ToolbarFragment toolFrag = new ToolbarFragment();

        fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
        fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
        fragmentTransaction.commitAllowingStateLoss();

        //add own fragment to the nav (abstract method)
        setOwnFragment();
    }
}

6

onActivityForResult () 메서드에서 조각을 표시하려고 할 때 항상 이것을 얻었습니다. 따라서 문제는 다음과 같습니다.

  1. 내 활동이 일시 중지되고 중지되었습니다. 즉, onSaveInstanceState ()가 이미 호출되었습니다 (Honeycomb 이전 및 Howncomb 장치 모두에 대해).
  2. 결과가 발생하면 트랜잭션이 조각을 표시하거나 숨기도록 하여이 IllegalStateException이 발생합니다.

내가 만든 것은 다음입니다.

  1. 원하는 동작이 수행되었는지 확인하기위한 값 추가 (예 : camere에서 사진 찍기-isPhotoTaken)-필요한 트랜잭션 수에 따라 부울 또는 정수 값일 수 있습니다.
  2. 재정의 된 onResumeFragments () 메서드에서 내 값을 확인하고 조각 트랜잭션을 수행 한 후 필요했습니다. 이 경우, onResumeFragments () 메소드에서 상태가 리턴되었으므로 onSaveInstanceState 후에 commit ()이 수행되지 않았습니다.

5

onconfigurationchanged로 문제를 해결했습니다. 요령은 명시 적으로 intent (카메라 의도 또는 다른 하나)를 호출했을 때 안드로이드 활동 수명주기에 따라 다릅니다. 이 경우 활동이 일시 중지되고 onsavedInstance가 호출됩니다. 활동이 활성화 된 위치가 아닌 다른 위치로 장치를 회전시킬 때; 조각 커밋과 같은 조각 작업을 수행하면 잘못된 상태 예외가 발생합니다. 그것에 대해 많은 불만이 있습니다. 그것은 안드로이드 활동 라이프 사이클 관리 및 적절한 메소드 호출에 관한 것입니다. 그것을 해결하기 위해 나는 이것을했다 : 1- 활동의 onsavedInstance 메소드를 재정의하고 현재 화면 방향 (세로 또는 가로)을 결정한 다음 활동이 일시 중지되기 전에 화면 방향을 설정하십시오. 그렇게하면 활동이 다른 활동에 의해 회전 된 경우 활동에 대한 화면 회전을 잠급니다. 2-, onresume 활동 방법을 재정의하고 onsaved 메소드가 호출 된 후 회전을 올바르게 처리하기 위해 구성에서 한 번 더 호출하도록 오리엔테이션 모드를 센서로 설정하십시오.

이 코드를 복사하여 활동에 붙여 넣어 처리 할 수 ​​있습니다.

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

    Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
    int orientation =this.getDisplayOrientation();
    //Lock the screen orientation to the current display orientation : Landscape or Potrait
    this.setRequestedOrientation(orientation);
}

//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device 
public int getDisplayOrientation() {
    Display getOrient = getWindowManager().getDefaultDisplay();

    int orientation = getOrient.getOrientation();

    // Sometimes you may get undefined orientation Value is 0
    // simple logic solves the problem compare the screen
    // X,Y Co-ordinates and determine the Orientation in such cases
    if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        Configuration config = getResources().getConfiguration();
        orientation = config.orientation;

        if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        // if height and widht of screen are equal then
        // it is square orientation
            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else { //if widht is less than height than it is portrait
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else { // if it is not any of the above it will defineitly be landscape
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        }
    }
    return orientation; // return value 1 is portrait and 2 is Landscape Mode
}

@Override
public void onResume() {
    super.onResume();
    Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} 

4

IllegalStateException을 얻는 것과 동일한 문제가 있었지만 commit ()에 대한 모든 호출을 commitAllowingStateLoss ()로 바꾸는 것이 도움이되지 않았습니다.

범인은 DialogFragment.show ()를 호출했습니다.

나는 그것을 둘러싸고

try {
    dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
    return;
}

그렇게 했어요 좋아, 대화 상자를 표시하지는 않지만이 경우에는 괜찮습니다.

앱에서 FragmentManager.beginTransaction ()을 처음 호출했지만 commit ()을 호출 한 적이없는 유일한 곳이므로 "commit ()"을 찾을 때 찾지 못했습니다.

재미있는 점은 사용자가 절대 앱을 떠나지 않는다는 것입니다. 대신 킬러는 AdMob 전면 광고가 게재되었습니다.


3
여기도 마찬가지입니다. 'commit'을 'commitAllowingStateLoss'로 대체하여 'show (FragmentManager manager, String tag)'메서드를 재정의했습니다. 대화 상자의 개인 속성 인 mDismissed 및 mShownByMe를 설정할 수 없기 때문에 무언가가 손실됩니다. 그러나 그것은 매번 작동하는 것 같습니다 :)
Francesco Ditrani

나는이 예외를 피할 수있는 DialogFragment의 대안 솔루션을 만들었습니다 : github.com/AndroidDeveloperLB/DialogShard
Android 개발자

4

그 문제에 대한 나의 해결책은

조각 추가 메소드에서 :

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
    guideMap = guideMapFragment.getMap();
    ...
}

@Override
public void onDestroyView() {
    SherlockFragmentActivity a = getSherlockActivity();
    if (a != null && guideMapFragment != null) {
        try {
            Log.i(LOGTAG, "Removing map fragment");
            a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
            guideMapFragment = null;
        } catch(IllegalStateException e) {
            Log.i(LOGTAG, "IllegalStateException on exit");
        }
    }
    super.onDestroyView();
}

나쁘지만 더 좋은 것을 찾지 못했습니다.


Trues .. 예외를 포착하면 응용 프로그램 충돌을 피할 수 있지만 화면에 남아 있거나 추가되지 않은 동작 문제가 발생합니다.
Marcos Vasconcelos

4
계속 스크롤하십시오. 진실은 거기에
anil

4

이 문제가 발생했지만이 문제는 commit 및 commitAllowStateLoss와 관련이 없다고 생각합니다.

다음 스택 추적 및 예외 메시지는 commit ()에 관한 것입니다.

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

그러나이 예외는 onBackPressed ()로 인해 발생했습니다.

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)

모두 checkStateLoss ()에 의해 발생했습니다.

private void checkStateLoss() {
    if (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);
    }

onSaveInstanceState 다음에 mStateSaved가 적용됩니다.

이 문제는 거의 발생하지 않습니다.이 문제가 발생하지 않았습니다. 문제가 다시 발생할 수 없습니다.

25517 문제를 발견 했습니다

다음과 같은 상황에서 발생했을 수 있습니다.

  1. 이전 키는 onSaveInstanceState 이후에 호출되지만 새 활동이 시작되기 전에 호출됩니다.

  2. 코드에서 onStop () 사용

문제의 근원이 무엇인지 잘 모르겠습니다. 그래서 못생긴 방법을 사용했습니다.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}

실제로 문제를 해결하지는 않았지만이 문제는 commit 및 commitAllowStateLoss와 관련이 없습니다.
oO_ox

4

내 앱에서 같은 문제가 발생했습니다. super.onBackPressed();이전 클래스에서 commitAllowingStateLoss()on을 호출하고 해당 클래스로 현재 클래스 에서 on을 호출하면이 문제가 해결되었습니다 .


2
감사합니다. 이 솔루션은 문제 commitAllowingStateLoss()대신 commit()
uisng

commitAllowingStateLoss () medium.com/@elye.project/…
swooby

3

사용자가 새 방향과 관련된 리소스를로드 할 수 있도록 화면을 회전하면 onSaveInstance가 호출됩니다.

이 사용자가 화면을 회전 한 후 뒤로 버튼을 누르면 가능할 수 있습니다 (이 사용자가 앱을 사용하는 동안 휴대 전화를 비틀었을 수도 있기 때문에)


2
구성 변경 (예 : 방향 변경)으로 인해이 예외가 발생할 수 있지만 근본 원인은 아닙니다.
Alex Lockwood


2

나에게서와 같은 문제가 있으며 하루 종일 모든 기사, 블로그 및 stackoverflow를 분석 한 후 간단한 해결책을 찾았습니다. savedInstanceState를 전혀 사용하지 마십시오. 한 줄의 코드가있는 조건입니다. 조각 코드에서 :

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....

2

이것은 조각을로드하려고하지만 활동이 상태를 onPause ()로 변경 될 때마다 발생합니다. 예를 들어 데이터를 가져 와서 활동에로드하려고하지만 사용자가 버튼을 클릭 할 때까지 다음 활동으로 옮겼습니다.

두 가지 방법으로이 문제를 해결할 수 있습니다

transaction.commit () 대신 transaction.commitAllowingStateLoss ()를 사용하여 조각을로드 할 수 있지만 수행 된 커밋 작업이 손실 될 수 있습니다.

또는

단편을로드 할 때 활동이 재개되고 일시 정지 상태가 아닌지 확인하십시오. 부울을 작성하고 활동이 onPause () 상태가 아닌지 확인하십시오.

@Override
public void onResume() {
    super.onResume();
    mIsResumed = true;
}

@Override
public void onPause() {
    mIsResumed = false;
    super.onPause();
}

그런 다음 조각을로드하는 동안 활동이 있는지 확인하고 활동이 포 그라운드 인 경우에만로드하십시오.

if(mIsResumed){
 //load the fragment
}

1

@gunar에게 감사하지만 더 좋은 방법이 있다고 생각합니다.

의사에 따르면 :

 * If you are committing a single transaction that does not modify the
 * fragment back stack, strongly consider using
 * {@link FragmentTransaction#commitNow()} instead. This can help avoid
 * unwanted side effects when other code in your app has pending committed
 * transactions that expect different timing.
 *
 * @return Returns true if there were any pending transactions to be
 * executed.
 */
public abstract boolean executePendingTransactions();

따라서 다음 commitNow을 교체하십시오.

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()

0

글쎄, 성공하지 않고 위의 모든 솔루션을 시도한 후에 (기본적으로 거래가 없기 때문에).

필자의 경우 FragmentManager를 요청할 때 때때로 회전시 AlertDialogs 및 ProgressDialog를 조각으로 사용하여 오류가 발생합니다.

비슷한 많은 게시물을 혼합하는 해결 방법을 발견했습니다.

FragmentActivity (이 경우 GenericActivity라고 함)에서 수행되는 3 단계 솔루션입니다.

private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    //To avoid bug for fragments: Step 2 of 3
    activity = new WeakReference<GenericActivity>(this);
}

@Override
public FragmentManager getSupportFragmentManager(){
    //To avoid bug for fragments: Step 3 of 3
    if (this == activity.get()) {
        return super.getSupportFragmentManager();
    }
    return activity.get().getSupportFragmentManager();
}

0

한 조각에서 startactivity를 사용하면이 예외가 발생합니다.

startactivityforresult를 사용하도록 변경하면 예외가 사라집니다. :)

그래서 그것을 고치는 쉬운 방법은 startActivityForResult API를 사용하는 것입니다 :)


0

지도 조각 활동에서 인 텐트 선택기를 취소하기 위해 뒤로 버튼을 누를 때이 예외가 발생했습니다. onResume () 코드를 조각을 초기화하고 트랜잭션을 커밋하는 코드를 onStart ()로 바꾸면이 문제가 해결되었으며 앱이 정상적으로 작동합니다. 도움이 되길 바랍니다.


0

이것은 Android 4.2 및 지원 라이브러리의 소스에서도 수정되었습니다. [*]

원인 및 해결 방법에 대한 자세한 내용은 다음 Google 버그 보고서를 참조하십시오. http://code.google.com/p/android/issues/detail?id=19917

지원 라이브러리를 사용하는 경우이 버그에 대해 걱정할 필요가 없습니다 (오랫 동안) [*]. 그러나 API를 직접 사용하고 (즉, 지원 라이브러리의 FragmentManager를 사용하지 않음) Android 4.2 미만의 API를 대상으로하는 경우 해결 방법 중 하나를 시도해야합니다.

[*] 작성 당시 Android SDK Manager는 여전히이 버그가있는 이전 버전을 배포하고 있습니다.

편집 나는 분명히이 답변을 다운 투표 한 사람을 혼란스럽게했기 때문에 여기에 설명을 추가 할 것입니다.

있습니다 이 예외가 발생 될 수 있습니다 여러 가지 (그러나 관련) 상황 . 위의 답변은 문제에서 논의 된 특정 인스턴스, 즉 Android의 버그를 참조하여 나중에 수정되었습니다. 다른 이유로이 예외가 발생하면 (조각 상태를 저장 한 후)하지 말아야 할 때 조각을 추가 / 제거하기 때문입니다. 이러한 상황에 처한 경우 " 중첩 된 조각-IllegalStateException"onSaveInstanceState 후에이 작업을 수행 할 수 없음 " "을 사용할 수 있습니다.



0

내 유스 케이스 : 리스너를 조각으로 사용하여 어떤 일이 발생했음을 활동에 알 렸습니다. 콜백 메소드에서 새로운 조각 커밋을 수행했습니다. 처음에는 완벽하게 작동합니다. 그러나 방향 변경시 활동은 저장된 인스턴스 상태로 다시 작성됩니다. 이 경우 프래그먼트가 다시 생성되지 않으면 프래그먼트에 오래된 파괴 된 리스너가 있음을 의미합니다. 어떤 식 으로든 콜백 메소드가 작동하면 트리거됩니다. 문제를 일으키는 활동을 파괴합니다. 해결책은 현재 라이브 활동으로 리스너를 단편으로 재설정하는 것입니다. 이것은 문제를 해결합니다.


0

내가 찾은 것은 다른 앱이 대화 상자 유형이고 터치가 백그라운드 앱으로 전송되도록 허용하면 거의 모든 백그라운드 앱 이이 오류와 충돌한다는 것입니다. 인스턴스가 저장 또는 복원 된 경우 트랜잭션이 수행 될 때마다 확인해야한다고 생각합니다.


0

내 경우에는 동일한 오류 예외가 발생하여 "onBackPressed ()"를 실행 가능 파일에 넣습니다 (모든보기를 사용할 수 있음).

myView.post(new Runnable() {
                    @Override
                    public void run() {
                        onBackPressed()
                    }
                });

이유를 이해하지 못하지만 작동합니다!


뷰에 게시하면 뷰가 올바르게 배치되고 화면에 그려진 후에 만 ​​Runnable이 실행됩니다. 이것은 종종 활동 자체가 완전히 재개되었음을 의미하므로 아무런 문제가 없습니다
Mercato

0

당신은 fragmentManager.popBackStackImmediate (); 활동이 일시 중지 된 경우 활동이 완료되지 않았지만 일시 정지되고 포 그라운드가 아닙니다. popBackStackImmediate () 전에 활동이 일시 중지되었는지 여부를 확인해야합니다.


0

나는 매우 흥미로운 것을 발견했다. 내 앱에는 휴대 전화 갤러리를 열 수있는 옵션이 있으며 장치는 사용할 앱을 묻습니다. 대화 상자에서 회색 영역을 클릭하면이 문제가 발생했습니다. 내 활동이 onPause, onSaveInstanceState에서 onResume으로 돌아가는 방식을 알았으므로 onCreateView를 방문하지 않습니다. onResume에서 거래를하고 있습니다. 그래서 내가 한 일은 Pause onPause에서 부정되고 있지만 onCreateView에서 true 인 플래그를 설정하는 것입니다. 플래그가 onResume 인 경우 onCommit을 수행하고, 그렇지 않으면 commitAllowingStateLoss를 수행하십시오. 계속해서 많은 시간을 낭비 할 수 있었지만 수명주기를 확인하고 싶었습니다. sdkversion 23 인 장치가 있는데이 문제가 발생하지 않지만 21 인 또 다른 장치가 있는데 거기에 있습니다.


-1

popBackStackImmediate 전에 FragmentActivity.onStart를 사용할 수 있습니다

이처럼 :

public void backStackFragment() {
    this.start();
    getFragmentManager().popBackStackImmediate();
}

public void start(){
    FragmentActivity a = getActivity();
    if(a instanceof DepositPlanPadActivity){
      ((DepositPlanPadActivity)a).onStart();
    }
    if(a instanceof SmallChangePlanPad){
            ((SmallChangePlanPad)a).onStart();
        }
        if(a instanceof UserCenterActivity){
            ((UserCenterActivity)a).onStart();
        }
    }

http://jorryliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.