Programming

IllegalStateException : ViewPager를 사용하여 onSaveInstanceState 후에이 조치를 수행 할 수 없습니다.

procodes 2020. 2. 15. 23:27
반응형

IllegalStateException : ViewPager를 사용하여 onSaveInstanceState 후에이 조치를 수행 할 수 없습니다.


시장의 앱에서 사용자 보고서를 받고 다음 예외를 제공합니다.

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.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
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:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

분명히 그것은 내가 사용하지 않는 FragmentManager와 관련이 있습니다. stacktrace에 내 클래스가 표시되지 않으므로이 예외가 발생하는 위치와 방지 방법을 모릅니다.

기록을 위해 : 나는 탭 호스트를 가지고 있으며 각 탭에는 활동간에 활동 그룹 전환이 있습니다.


내 답변을 여기에서 확인 하십시오 . 기본적으로 나는 단지 :

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

에 전화를하지 마십시오 super()saveInstanceState방법. 이것은 엉망이되었습니다 ...

지원 패키지 의 알려진 버그 입니다.

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

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

결국 적절한 해결책은 (주석에서 볼 수 있듯이) 다음을 사용하는 것입니다.

transaction.commitAllowingStateLoss();

추가하거나 수행 할 때 FragmentTransaction을 일으키는 것을 Exception.


비슷한 오류 메시지와 관련된 많은 문제가 있습니다. 이 특정 스택 추적의 두 번째 줄을 확인하십시오. 이 예외는 특히에 대한 호출과 관련이 FragmentManagerImpl.popBackStackImmediate있습니다.

세션 상태가 이미 저장 되어 있으면 이 메소드 호출 popBackStack항상 실패 IllegalStateException합니다. 소스를 확인하십시오. 이 예외가 발생하는 것을 막기 위해 할 수있는 일은 없습니다.

  • 통화를 삭제 super.onSaveInstanceState해도 도움이되지 않습니다.
  • 를 사용하여 조각을 만드는 commitAllowingStateLoss것은 도움이되지 않습니다.

문제를 관찰 한 방법은 다음과 같습니다.

  • 제출 버튼이있는 양식이 있습니다.
  • 버튼을 클릭하면 대화 상자가 생성되고 비동기 프로세스가 시작됩니다.
  • 사용자는 프로세스가 완료되기 전에 홈 키를 클릭합니다 onSaveInstanceState.
  • 프로세스가 완료되고 콜백이 이루어지고 popBackStackImmediate시도됩니다.
  • IllegalStateException 던졌습니다.

내가 해결하기 위해 한 일은 다음과 같습니다.

IllegalStateException콜백 을 피할 수 없으므로 catch & 무시하십시오.

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There's no way to avoid getting this if saveInstanceState has already been called.
}

앱이 중단되는 것을 막기에 충분합니다. 그러나 이제 사용자는 앱을 복원하고 자신이 눌렀다 고 생각한 버튼이 전혀 눌리지 않았 음을 알 수 있습니다 (생각합니다). 양식 조각이 여전히 표시됩니다!

이 문제를 해결하려면 대화 상자를 만들 때 프로세스가 시작되었음을 나타내는 상태를 만드십시오.

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

이 상태를 번들에 저장하십시오.

@Override
public void onSaveInstanceState(Bundle outState) {
    ...
    outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

다시로드하는 것을 잊지 마십시오 onViewCreated

그런 다음 다시 시작할 때 제출이 이전에 시도 된 경우 조각을 롤백하십시오. 이를 통해 사용자는 제출되지 않은 양식으로 돌아가는 것을 방지 할 수 있습니다.

@Override
public void onResume() {
    super.onResume();
    if (submitPressed) {
        // no need to try-catch this, because we are not in a callback
        activity.getSupportFragmentManager().popBackStackImmediate(name);
        submitPressed = false;
    }
}

isFinishing()조각을 표시하기 전에 활동을 확인 하고에주의하십시오 commitAllowingStateLoss().

예:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}

이 문제에 대한 다른 해결책이 있습니다.

개인 멤버 변수를 사용하면 반환 된 데이터를 의도로 설정 한 다음 super.onResume (); 이후에 처리 할 수 ​​있습니다.

이렇게 :

private Intent mOnActivityResultIntent = null; 

@Override
protected void onResume() {
    super.onResume();
    if(mOnActivityResultIntent != null){
        ... do things ...
        mOnActivityResultIntent = null;
    }
 }

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        mOnActivityResultIntent = data;
    }
}

2017 년 10 월이며 Google은 Lifecycle 구성 요소라고하는 새로운 기능으로 Android 지원 라이브러리를 만듭니다. 'onSaveInstanceState 후에이 작업을 수행 할 수 없습니다'문제에 대한 새로운 아이디어를 제공합니다.

한마디로 :

  • 수명주기 구성 요소를 사용하여 프래그먼트를 팝업하기에 적합한 시간인지 판별하십시오.

Explain이있는 더 긴 버전 :

  • 왜이 문제가 발생합니까?

    그것은 당신이 FragmentManager당신의 활동 (내가 생각하는 조각을 보유 할 것입니다)에서 당신을 위해 트랜잭션을 커밋하려고 사용하기 때문입니다. 일반적으로 이것은 호스트 조각 활동이 이미 savedInstanceState메서드를 호출 하는 동안 곧 나오는 조각에 대한 트랜잭션을 시도하는 것처럼 보일 것입니다 (사용자는 홈 버튼을 터치하여 활동이 호출되므로 onStop()내 경우에는 이유입니다)

    일반적으로이 문제는 발생하지 않아야합니다. 우리는 항상 처음에 조각을 활동에로드하려고합니다 onCreate(). 그러나 때로는 이런 일이 발생합니다 . 특히 해당 활동에로드 할 조각을 결정할 수 없거나 AsyncTask블록 에서 조각을로드하려고 할 때 (또는 시간이 조금 걸립니다). 프래그먼트 트랜잭션이 실제로 발생하기 전의 시간이지만 액티비티의 onCreate()메소드 후에 사용자는 무엇이든 할 수 있습니다. 사용자가 홈 버튼을 누르면 활동의 onSavedInstanceState()메소드 가 트리거되고 can not perform this action충돌이 발생합니다.

    이 문제에 대해 더 깊이 알고 싶다면이 블로그 게시물을 살펴 보는 것이 좋습니다 . 소스 코드 레이어 내부를 자세히 살펴보고 이에 대해 많은 것을 설명합니다. 또한 commitAllowingStateLoss()이 충돌을 해결 하기 위해 방법을 사용하지 않아야하는 이유를 제공합니다 (코드에 아무런 도움이되지 않음)

  • 이 문제를 해결하는 방법?

    • commitAllowingStateLoss()조각을로드 하는 방법을 사용해야합니까 ? 아니해서는 안됩니다 .

    • onSaveInstanceState메서드를 재정의해야하고 그 super안에있는 메서드를 무시 해야합니까 ? 아니해서는 안됩니다 .

    • isFinishing호스트 활동이 프래그먼트 트랜잭션에 적합한 순간인지 확인 하기 위해 마법의 내부 활동을 사용해야 합니까? 예, 이것은 올바른 방법 처럼 보입니다 .

  • Lifecycle 구성 요소가 수행 할 수있는 작업을 살펴보십시오 .

    기본적으로 Google은 AppCompatActivity클래스 (및 프로젝트에서 사용해야하는 다른 기본 클래스) 내에서 일부 구현을 수행 하므로 현재 수명주기 상태를 보다 쉽게 확인할 수 있습니다. 문제를 다시 살펴보십시오. 왜이 문제가 발생합니까? 잘못된 타이밍에 무언가를하기 때문입니다. 그래서 우리는 그렇게하지 않으려 고 노력하며이 문제는 사라질 것입니다.

    나는 내 자신의 프로젝트를 위해 약간의 코드를 작성한다 LifeCycle. 나는 Kotlin으로 코딩합니다.

val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.

fun dispatchFragment(frag: Fragment) {
    hostActivity?.let {
       if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
           showFragment(frag)
       }
    }
}

private fun showFragment(frag: Fragment) {
    hostActivity?.let {
        Transaction.begin(it, R.id.frag_container)
                .show(frag)
                .commit()
    }

위에 보여 주듯이. 호스트 활동의 수명주기 상태를 확인합니다. 지원 라이브러리 내에 Lifecycle 구성 요소가 있으면 더 구체적 일 수 있습니다. 코드 lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)는 현재 상태가 적어도 onResume늦지 않은 경우를 의미 합니다. 어떤 다른 수명 상태 (예 :) 동안 내 메소드가 실행되지 않도록합니다 onStop.

  • 다 되었습니까?

    당연히 아니지. 내가 보여준 코드는 응용 프로그램 충돌을 막는 새로운 방법을 알려줍니다. 그러나의 상태로 전환 onStop되면 해당 코드 줄은 아무런 작업을 수행하지 않으므로 화면에 아무것도 표시되지 않습니다. 사용자가 응용 프로그램으로 돌아 오면 빈 화면이 표시됩니다. 빈 화면으로 조각이 전혀 표시되지 않습니다. 나쁜 경험입니다 (예 : 충돌보다 조금 낫습니다).

    그래서 여기에 더 좋은 것이있을 수 있기를 바랍니다. 앱이 늦게 수명 상태에 도달하면 충돌하지 않습니다 onResume. 트랜잭션 방법은 수명 상태를 인식합니다. 또한 액티비티는 사용자가 앱으로 돌아온 후 해당 조각 트랜잭션 작업을 계속 완료하려고 시도합니다.

    이 방법에 더 많은 것을 추가합니다.

class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
    private val hostActivity: FragmentActivity? = _host
    private val lifeCycle: Lifecycle? = _host.lifecycle
    private val profilePendingList = mutableListOf<BaseFragment>()

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume() {
        if (profilePendingList.isNotEmpty()) {
            showFragment(profilePendingList.last())
        }
    }

    fun dispatcherFragment(frag: BaseFragment) {
        if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
            showFragment(frag)
        } else {
            profilePendingList.clear()
            profilePendingList.add(frag)
        }
    }

    private fun showFragment(frag: BaseFragment) {
        hostActivity?.let {
            Transaction.begin(it, R.id.frag_container)
                    .show(frag)
                    .commit()
        }
    }
}

dispatcher클래스 내부에 목록을 유지 관리하여 해당 조각을 저장하면 거래 작업을 완료 할 기회가 없습니다. 사용자가 홈 화면에서 돌아와서 여전히 실행 대기중인 조각이 있음을 발견 resume()하면 @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)주석 아래의 방법으로 이동합니다 . 이제 예상대로 작동해야한다고 생각합니다.


짧고 효과적인 해결책 :

간단한 단계를 따르십시오

단계

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

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

2 단계 : 사용 fragmentTransaction.commitAllowingStateLoss( );

fragmentTransaction.commit( );조각화 작업 대신 .


주의 하여 사용 transaction.commitAllowingStateLoss()하면 사용자에게 좋지 않은 경험이 될 수 있습니다. 이 예외가 발생하는 이유에 대한 자세한 내용은 이 게시물을 참조하십시오 .


이런 종류의 문제에 대한 더러운 해결책을 찾았습니다. ActivityGroups어떤 이유로 든 계속 유지하고 싶다면 (시간 제한이있는 이유) 구현하면됩니다.

public void onBackPressed() {}

당신을에 Activity어떤 할 back코드가. 구형 장치에 그러한 메소드가없는 경우에도이 메소드는 최신 메소드에 의해 호출됩니다.


commitAllowingStateLoss ()를 사용하지 마십시오. UI 상태가 사용자에게 예기치 않게 변경 될 수있는 경우에만 사용해야합니다.

https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss ()

트랜잭션이 parentFragment의 ChildFragmentManager에서 발생하는 경우 parentFragment.isResume ()을 사용 하여 대신 확인하십시오.

if (parentFragment.isResume()) {
    DummyFragment dummyFragment = DummyFragment.newInstance();
    transaction = childFragmentManager.BeginTransaction();
    trans.Replace(Resource.Id.fragmentContainer, startFragment);
}

비슷한 문제가 있었는데 시나리오는 다음과 같습니다.

  • 내 활동은 목록 조각을 추가 / 교체합니다.
  • 각 목록 조각에는 목록 항목을 클릭 할 때 활동을 알리기 위해 활동에 대한 참조가 있습니다 (관찰자 패턴).
  • 각 목록 조각은 setRetainInstance (true)를 호출 합니다. 그것의 에서 onCreate 방법.

활동onCreate 메소드는 다음 과 같습니다.

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }

구성이 변경 될 때 (장치 회전) 활동이 작성되고 단편 관리자의 히스토리에서 기본 단편이 검색되고 동시에 단편이 이미 파괴 된 활동에 대한 OLD 참조를 가지고 있기 때문에 예외가 발생했습니다.

구현을 이것으로 변경하면 문제가 해결되었습니다.

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }
        mMainFragment.setOnSelectionChangedListener(this);

프래그먼트가 오래된 파괴 된 활동 인스턴스에 대한 참조를 갖는 상황을 피하기 위해 활동이 작성 될 때마다 리스너를 설정해야합니다.


지도 조각 활동에서 인 텐트 선택기를 취소하기 위해 뒤로 버튼을 누를 때이 예외가 발생했습니다. onResume 코드를 onstart ()로 바꾸어이 문제를 해결했으며 앱이 정상적으로 작동합니다.


에서 상속받은 경우 다음 FragmentActivity에서 수퍼 클래스를 호출해야합니다 onActivityResult().

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    ...
}

이 작업을 수행하지 않고 해당 방법으로 조각 대화 상자를 표시하려고하면 OP가 표시 될 수 있습니다 IllegalStateException. (솔직히 말하면 슈퍼 호출이 문제를 해결하는 이유를모르겠습니다 . onActivityResult()전에 호출 onResume()되었으므로 여전히 조각 대화 상자를 표시해서는 안됩니다.)


사용하는 transaction.commitAllowingStateLoss();것이 최선의 해결책은 아니라고 생각 합니다. 액티비티의 구성이 변경되고 프래그먼트 onSavedInstanceState()가 호출 된 후 비동기 콜백 메소드가 프래그먼트를 커밋하려고 시도하면 이 예외가 발생 합니다.

간단한 해결책은 활동이 구성을 변경하는지 여부를 확인할 수 있습니다

예를 들어 확인 isChangingConfigurations()

if(!isChangingConfigurations()) { //commit transaction. }

체크 아웃 아니라 링크를


onActivityResult에서 FragmentTransaction을 수행하는 경우 onActivityResult 내에서 부울 값을 설정할 수 있으며 onResume에서 부울 값을 기반으로 FragmentTransaction을 수행 할 수 있습니다. 아래 코드를 참조하십시오.

@Override
protected void onResume() {
    super.onResume;
    if(isSwitchFragment){
        isSwitchFragment=false;
        bottomNavigationView.getTabAt(POS_FEED).select();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
        isSwitchFragment=true;
    }
}

지원 라이브러리 버전에서 시작 24.0.0 당신이 호출 할 수 있습니다 FragmentTransaction.commitNow()를 호출하는 대신 동 기적으로 트랜잭션을 커밋 방법 commit()다음을 executePendingTransactions(). 마찬가지로 문서는 더 나은이 방법은 말한다 :

commitNow를 호출하는 것이 commit ()을 호출 한 다음 executePendingTransactions ()를 호출하는 것보다 낫습니다. 후자는 원하는 동작인지 여부에 관계없이 현재 보류중인 모든 트랜잭션을 커밋하려고 시도하는 부작용이 있습니다.


필자의 경우에서 찾은 가장 부드럽고 가장 간단한 해결책은 활동 결과에 응답하여 문제가되는 조각을 스택에서 튀어 나오는 것을 피하는 것이 었습니다. 그래서 내 전화를 변경 onActivityResult():

popMyFragmentAndMoveOn();

이에:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    public void run() {
        popMyFragmentAndMoveOn();
    }
}

내 경우에 도움이되었습니다.


활동에 프래그먼트를로드하려고 할 때마다 활동이 재개되고 일시 중지 상태가 아닌지 확인하십시오. 일시 중지 상태에서는 수행 된 커밋 작업이 손실 될 수 있습니다.

transaction.commit () 대신 transaction.commitAllowingStateLoss ()를 사용하여 조각을로드 할 수 있습니다.

또는

부울을 작성하고 활동이 일시 중지되지 않는지 확인하십시오.

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

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

그런 다음 조각 검사를로드하는 동안

if(mIsResumed){
//load the your fragment
}

이 문제를 우회하기 위해 Google I / O 2018에 도입 된 Navigation Architecture 구성 요소를 사용할 수 있습니다 . Navigation Architecture 구성 요소 는 Android 앱에서 탐색 구현을 단순화합니다.


의례 : IllegalStateException에 대한 솔루션

이 문제는 많은 시간 동안 나를 귀찮게했지만 다행히도 구체적인 해결책을 찾았습니다. 이에 대한 자세한 설명은 여기에 있습니다 .

commitAllowStateloss ()를 사용하면이 예외를 방지 할 수 있지만 UI 불규칙성이 발생할 수 있습니다. 지금까지 Activity 상태가 손실 된 후 프래그먼트를 커밋하려고 할 때 IllegalStateException이 발생한다는 것을 이해했습니다. 따라서 상태가 복원 될 때까지 트랜잭션을 지연시켜야합니다. . 간단하게 이렇게 할 수 있습니다

두 개의 개인 부울 변수 선언

 public class MainActivity extends AppCompatActivity {

    //Boolean variable to mark if the transaction is safe
    private boolean isTransactionSafe;

    //Boolean variable to mark if there is any transaction pending
    private boolean isTransactionPending;

이제 onPostResume () 및 onPause에서 부울 변수 isTransactionSafe를 설정 및 설정 해제했습니다. 아이디어는 활동이 포 그라운드에있을 때만 trasnsaction을 안전하게 표시하여 상태 손실의 가능성이 없도록하는 것입니다.

/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
 */
public void onPostResume(){
    super.onPostResume();
    isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
 */

public void onPause(){
    super.onPause();
    isTransactionSafe=false;

}

private void commitFragment(){
    if(isTransactionSafe) {
        MyFragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.frame, myFragment);
        fragmentTransaction.commit();
    }
}

-지금까지 수행 한 작업은 IllegalStateException에서 저장되지만 활동이 백그라운드로 이동 한 후 commitAllowStateloss ()와 같이 수행되면 트랜잭션이 손실됩니다. 이를 돕기 위해 isTransactionPending 부울 변수가 있습니다.

public void onPostResume(){
   super.onPostResume();
   isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
   if (isTransactionPending) {
      commitFragment();
   }
}


private void commitFragment(){

 if(isTransactionSafe) {
     MyFragment myFragment = new MyFragment();
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     fragmentTransaction.add(R.id.frame, myFragment);
     fragmentTransaction.commit();
     isTransactionPending=false;
 }else {
     /*
     If any transaction is not done because the activity is in background. We set the
     isTransactionPending variable to true so that we can pick this up when we come back to
foreground
     */
     isTransactionPending=true;
 }
}

@Anthonyeef 훌륭한 답변과 관련하여 Java의 샘플 코드는 다음과 같습니다.

private boolean shouldShowFragmentInOnResume;

private void someMethodThatShowsTheFragment() {

    if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
        showFragment();
    } else {
        shouldShowFragmentInOnResume = true;
    }
}

private void showFragment() {
    //Your code here
}

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

    if (shouldShowFragmentInOnResume) {
        shouldShowFragmentInOnResume = false;
        showFragment();
    }
}

내 경우에는 onActivityResult라는 재정의 메서드 에서이 오류가 발생했습니다. 파고 난 후에 나는 아마도 ' 슈퍼 ' 를 호출해야한다고 생각했습니다 .
나는 그것을 추가했고 그것은 효과가 있었다.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data); //<--- THIS IS THE SUPPER CALL
    if (resultCode == Activity.RESULT_OK && requestCode == 0) {
        mostrarFragment(FiltroFragment.newInstance())
    }

}

어쩌면 코드 전에 수행중인 재정의에 '슈퍼'를 추가해야 할 수도 있습니다.


활동에 이것을 추가하십시오

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (outState.isEmpty()) {
        // Work-around for a pre-Android 4.2 bug
        outState.putBoolean("bug:fix", true);
    }
}

또한이 문제가 발생하여 상황 FragmentActivity이 변경 될 때마다 문제가 발생합니다 (예 : 화면 방향 변경 등). 따라서 가장 좋은 해결책은에서 컨텍스트를 업데이트하는 것 FragmentActivity입니다.


기본 조각을 만들고 앱의 모든 조각을 확장합니다.

public class BaseFragment extends Fragment {

    private boolean mStateSaved;

    @CallSuper
    @Override
    public void onSaveInstanceState(Bundle outState) {
        mStateSaved = true;
        super.onSaveInstanceState(outState);
    }

    /**
     * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
     * would otherwise occur.
     */
    public void showAllowingStateLoss(FragmentManager manager, String tag) {
        // API 26 added this convenient method
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager.isStateSaved()) {
                return;
            }
        }

        if (mStateSaved) {
            return;
        }

        show(manager, tag);
    }
}

그런 다음 조각을 표시하려고 할 때 showAllowingStateLoss대신show

이처럼 :

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

이 PR 에서이 솔루션을 찾았습니다 : https://github.com/googlesamples/easypermissions/pull/170/files


모든 경우에 도움이되는지 확실하지 않은 또 다른 가능한 해결 방법은 다음과 같습니다 ( here ).

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        final View rootView = findViewById(android.R.id.content);
        if (rootView != null) {
            rootView.cancelPendingInputEvents();
        }
    }
}

@Ovidiu Latcu에 의해 허용되는 답변이 있지만 잠시 후에도 오류가 계속 발생합니다.

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

Crashlytics가 여전히이 이상한 오류 메시지를 보냅니다.

그러나 오류는 이제 버전 7 이상에서만 발생합니다 (누가) 내 수정은 fragmentTransaction에서 commit () 대신 commitAllowingStateLoss () 를 사용하는 것이 었습니다 .

게시물 은 commitAllowingStateLoss ()에 도움이되었으며 다시는 조각 문제가 발생하지 않았습니다.

요약하면 여기에서 허용되는 답변은 Nougat 이전 Android 버전에서 작동 할 수 있습니다.

이렇게하면 몇 시간의 검색 시간을 절약 할 수 있습니다. 행복한 코딩. <3 건배


나는 똑같은 문제가 있었다. 이전 활동의 파괴로 인해 발생했습니다. 이전 활동을 뒷받침했을 때 파괴되었습니다. 기본 활동 (WRONG)을 넣었습니다

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
    onCreateDrawerActivity(savedInstanceState);
}

나는 그것을 시작에 넣었다.

@Override
protected void onStart() {
    super.onStart();
    SpinnerCustom2.setFragmentManager(getSupportFragmentManager());

}

예외는 여기에서 발생합니다 (FragmentActivity에서).

@Override
public void onBackPressed() {
    if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
        super.onBackPressed();
    }
}

에서 FragmentManager.popBackStatckImmediate(), FragmentManager.checkStateLoss()먼저라고합니다. 의 원인입니다 IllegalStateException. 아래 구현을 참조하십시오.

private void checkStateLoss() {
    if (mStateSaved) { // Boom!
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }
}

플래그를 사용하여 Activity의 현재 상태를 표시 하여이 문제를 간단히 해결합니다. 내 해결책은 다음과 같습니다.

public class MainActivity extends AppCompatActivity {
    /**
     * A flag that marks whether current Activity has saved its instance state
     */
    private boolean mHasSaveInstanceState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mHasSaveInstanceState = true;
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mHasSaveInstanceState = false;
    }

    @Override
    public void onBackPressed() {
        if (!mHasSaveInstanceState) {
            // avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException
            super.onBackPressed();
        }
    }
}

popBackStack () 또는 popBackStackImmediate () 메소드와 충돌하는 경우 다음을 사용하여 fixt를 시도하십시오.

        if (!fragmentManager.isStateSaved()) {
            fragmentManager.popBackStackImmediate();
        }

이것은 나에게도 효과가 있습니다.


@Gian Gomen 제 경우에는 SUPER가 문제를 해결합니다. commitAllowingStateLoss ()보다 더 정확한 해결책 인 것 같습니다. 문제를 해결하고 숨기지 않기 때문입니다.

@Override
public void onRequestPermissionsResult(
     final int requestCode,
     @NonNull final String[] permissions, 
     @NonNull final int[] grantResults
) {
        super.onRequestPermissionsResult(requestCode,permissions, grantResults); //<--- Without this line crash 
        switch (requestCode) {
            case Constants.REQUEST_CODE_PERMISSION_STORAGE:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    onPermissionGranted(Constants.REQUEST_CODE_PERMISSION_STORAGE);
                }
                break;
        }

참고 URL : https://stackoverflow.com/questions/7575921/illegalstateexception-can-not-perform-this-action-after-onsaveinstancestate-wit



반응형