programing

잘못된 상태 예외:ViewPager를 사용하여 인스턴스 상태 저장 후 이 작업을 수행할 수 없습니다.

showcode 2023. 6. 4. 10:42
반응형

잘못된 상태 예외:ViewPager를 사용하여 인스턴스 상태 저장 후 이 작업을 수행할 수 없습니다.

시장에 있는 내 앱에서 다음과 같은 예외를 제공하는 사용자 보고서를 받고 있습니다.

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)

제가 사용하지 않는 Fragment Manager와 관련이 있는 것 같습니다.스택 추적에는 내 클래스가 표시되지 않으므로 이 예외가 발생하는 위치와 방지 방법을 알 수 없습니다.

참고로:탭 호스트가 있으며 각 탭에는 활동 그룹이 전환되어 있습니다.

여기서 제 답변을 확인해 주세요.기본적으로 저는 다음과 같이 해야 했습니다.

@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이 호출됩니다.
  • 되며 "Callback"이 됩니다.popBackStackImmediate가 시도되었습니다.
  • IllegalStateException던져집니다.

이 문제를 해결하기 위해 제가 한 일은 다음과 같습니다.

수 없기 때문에IllegalStateException콜백에서, 그것을 잡고 무시하세요.

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();
}

2017년 10월, Google은 라이프사이클 구성요소라고 하는 새로운 기능으로 Android 지원 라이브러리를 만듭니다.인스턴스 상태 저장 후 이 작업을 수행할 수 없음' 문제에 대한 몇 가지 새로운 아이디어를 제공합니다.

간단히 말해서:

  • 라이프사이클 구성 요소를 사용하여 조각을 팝업하기에 적절한 시기인지 확인합니다.

설명이 포함된 긴 버전:

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

    그것은 당신이 사용하려고 하기 때문입니다.FragmentManager당신의 활동(당신의 조각을 유지할 것으로 예상되나요?)에서 조각에 대한 트랜잭션을 커밋합니다.일반적으로 호스트 활동이 이미 호출되는 동안 다음 조각에 대해 약간의 트랜잭션을 수행하려고 하는 것처럼 보입니다.savedInstanceStatemethod 홈이 호출될 수 .)onStop()나의 경우에는 그것이 이유입니다.)

    일반적으로 이 문제는 발생하지 않아야 합니다. 우리는 항상 처음에 조각을 활동에 로드하려고 노력합니다. 예를 들어,onCreate()방법은 이것을 위한 완벽한 장소입니다.그러나 때때로 이런 일이 발생합니다. 특히 해당 활동에 어떤 조각을 로드할지 결정할 수 없거나, 또는 특정 활동에서 조각을 로드하려고 할 때는 더욱 그렇습니다.AsyncTask차단(또는 시간이 조금 걸립니다).조각 트랜잭션이 실제로 발생하기 전, 그러나 활동이 종료된 후의 시간.onCreate()방법, 사용자는 무엇이든 할 수 있습니다.가 홈 버튼을 됩니다.onSavedInstanceState()방을, 있것니다가 입니다.can not perform this action와르르 무너지다

    이 문제에 대해 더 자세히 알고 싶은 사람이 있다면, 블로그 게시물을 살펴보라고 제안합니다.그것은 소스 코드 층 내부를 깊이 들여다보고 그것에 대해 많은 것을 설명합니다.또한, 그것은 당신이 사용하지 말아야 하는 이유를 제공합니다.commitAllowingStateLoss()이 되는 도 없습니다.)을 참조하십시오.

  • 어떻게 고칠까요?

    • 을 사용해야 ?commitAllowingStateLoss()fragment를 로드하는 방법?아니요, 그러면 안 됩니다.

    • 오버라이드해야 합니까?onSaveInstanceState 무시, 시무super그 안에 있는 방법?아니요, 그러면 안 됩니다.

    • 의 마을쓸까요를 해야 합니까?isFinishing내부 활동, 호스트 활동이 조각 트랜잭션에 적합한 시점에 있는지 확인하기 위해?네, 이것이 올바른 방법인 것 같습니다.

  • 라이프사이클 구성요소가 수행할 수 있는 기능을 살펴봅니다.

    기본적으로, 구글은 내부에서 약간의 구현을 합니다.AppCompatActivity클래스(및 프로젝트에서 사용해야 하는 다른 기본 클래스)를 사용하여 현재 라이프사이클 상태를 쉽게 확인할 수 있습니다.우리의 문제를 돌아보세요: 왜 이런 문제가 발생할까요?우리가 잘못된 타이밍에 무언가를 하기 때문입니다.그래서 우리는 그것을 하지 않으려고 노력합니다. 그러면 이 문제는 사라질 것입니다.

    제 . 에 제가 . 여기 제가 사용하는 것이 있습니다.LifeCycle코틀린에서 코드를 칩니다.

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()
    }

위에서 보여드린 것처럼.호스트 활동의 라이프사이클 상태를 확인하겠습니다.지원 라이브러리 내의 라이프사이클 구성요소를 사용하면 이를 보다 구체적으로 파악할 수 있습니다.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)평균, 현재 상태가 최소한인 경우onResume그보다 늦지 않았나요?그래서 내 방법은 다른 생명 상태에서 실행되지 않을 것입니다.onStop).

  • 다 됐나요?

    물론 그렇지 않습니다.제가 보여드린 코드는 응용 프로그램이 중단되는 것을 방지하는 새로운 방법을 알려줍니다.하지만 만약 그것이 만약의 상태로 간다면.onStop코드 라인이 작동하지 않아 화면에 아무것도 표시되지 않습니다.사용자가 애플리케이션으로 돌아오면 빈 화면이 나타납니다. 빈 호스트 활동은 조각이 전혀 표시되지 않습니다.그것은 나쁜 경험입니다. (네, 충돌보다 약간 낫습니다.)

    에 더 좋은 요: 되면 거예요. 그서다저나수되바다: 다것다상충면않지을니입하돌가태명중에래랍니보앱음이기를있는이여은것기좋더에▁won▁so다▁app것▁state▁if니입▁than않▁nicer▁it:▁i▁later그▁something을▁be▁here.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)ㅠㅠ 했던 대로 해야 할 것.이제는 제가 예상했던 대로 작동해야 한다고 생각합니다.

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

개인 멤버 변수를 사용하여 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;
    }
}

짧고 효과적인 솔루션:

간단한 단계를 따릅니다.

스텝

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

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

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

fragmentTransaction.commit( );조각 작업을 수행하는 동안.

주의, 사용transaction.commitAllowingStateLoss()사용자에게 좋지 않은 결과를 초래할 수 있습니다.이 예외가 발생하는 이유에 대한 자세한 내용은 이 게시물을 참조하십시오.

저는 이런 종류의 문제에 대한 더러운 해결책을 찾았습니다.만약 당신이 여전히 당신의 것을 유지하고 싶다면.ActivityGroups이유가 무엇이든 간에(나는 시간 제한 이유가 있었다), 당신은 그냥 구현합니다.

public void onBackPressed() {}

의 신의에Activity그리고 좀 해보세요.back장치에 합니다.이전 장치에 이러한 메서드가 없는 경우에도 최신 장치에서 이 메서드를 호출합니다.

커밋 사용 안 함StateLoss()를 허용하면 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);
}

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

  • 내 활동이 목록 조각을 추가/바꾸는 중입니다.
  • 각 목록 조각에는 목록 항목(관찰자 패턴)을 클릭할 때 활동을 알리기 위한 활동에 대한 참조가 있습니다.
  • 각 목록 조각은 onCreate 메서드에서 setRetainInstance(true)를 호출합니다.

활동의 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();
        }

구성이 변경되면(장치가 회전됨) 활동이 생성되고 fragment 관리자의 기록에서 기본 fragment가 검색되며 동시에 fragment가 이미 파괴된 활동에 대한 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);

조각이 오래된 삭제된 활동 인스턴스에 대한 참조를 가지는 상황을 방지하기 위해 활동이 작성될 때마다 수신기를 설정해야 합니다.

는에서 경우FragmentActivity당신은 슈퍼클래스를 불러와야 합니다.onActivityResult():

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

그의 fragment가 수 .IllegalStateException(솔직히 슈퍼콜이 왜 문제를 해결하는지 잘 모르겠습니다. onActivityResult() 앞에 됩니다.onResume()따라서 fragment 대화 상자를 표시하는 것은 없습니다.

다음 시간 이후에 조각 트랜잭션을 실행하면 안 됩니다! 다음 시간 이후에 트랜잭션을 실행할 수 있는 콜백이 없는지 확인하십시오.onStop()다음과 같은 접근법으로 문제를 회피하려고 하지 말고 이유를 해결하는 것이 좋습니다..commitAllowingStateLoss()

제 경우에 제가 찾은 가장 부드럽고 간단한 해결책은 활동 결과에 반응하여 문제가 되는 조각을 스택에서 터뜨리지 않는 것이었습니다. 제 에서 이 것.onActivityResult():

popMyFragmentAndMoveOn();

대상:

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

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

지도 조각 활동에서 의도 선택을 취소하기 위해 뒤로 버튼을 눌렀을 때 이 예외가 발생했습니다.나는 onResume(fragment를 초기화하던 곳)의 코드를 onstart()로 교체하여 이 문제를 해결했고 앱은 정상적으로 작동합니다.도움이 되길 바랍니다.

제공: 불법 상태 예외에 대한 해결책

이 문제는 오랫동안 저를 짜증나게 했지만, 다행히도 저는 그것에 대한 구체적인 해결책을 가지고 왔습니다.자세한 설명은 여기 있습니다.

커밋 사용AllowStateloss()는 이 예외를 방지할 수 있지만 UI가 잘못될 수 있습니다.지금까지 활동 상태가 손실된 후 조각을 커밋하려고 하면 불법 상태 예외가 발생한다는 것을 알고 있으므로 상태가 복구될 때까지 트랜잭션을 지연시켜야 합니다.이렇게 간단히 할 수 있습니다.

두 개의 전용 부울 변수 선언

 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에서 부울 변수는 TransactionSafe입니다.아이디어는 활동이 전면에 있을 때만 거래를 안전하게 표시하여 상태 손실의 가능성이 없도록 하는 것입니다.

/*
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();
    }
}

-지금까지 우리가 한 일은 불법적인 상태 예외에서 벗어날 수 있지만, 활동이 백그라운드로 이동한 후에 수행되면 커밋과 같은 거래가 손실됩니다.상태 손실 허용().이를 지원하기 위해 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;
 }
}

제생는에를 사용하는 것 요.transaction.commitAllowingStateLoss();최선의 해결책이 아닙니다.의 구성이 변경되고 fragment화된 경우 합니다.onSavedInstanceState()호출된 다음 비동기 콜백 메서드가 fragment를 커밋하려고 합니다.

활동이 구성을 변경하는지 여부를 확인하는 간단한 솔루션

①: 표를 확인합니다.isChangingConfigurations()

예.

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

이 링크도 확인하십시오.

활동에 단편을 로드할 때마다 활동이 재개되고 일시 중지 상태가 아닌지 확인합니다.일시 중지 상태에서는 수행된 커밋 작업이 손실될 수 있습니다.

transaction.commit을 사용할 수 있습니다.transaction.commit() 대신 StateLoss()가 fragment를 로드하도록 허용합니다.

또는

부울을 만들고 활동이 일시 중지되지 않는지 확인합니다.

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

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

fragment 검사를 로드하는 동안

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

onActivityResult에서 일부 FragmentTransaction을 수행하는 경우에는 ActivityResult에서 부울 값을 설정한 다음 Resume에서 부울 값을 기준으로 부울 트랜잭션을 수행할 수 있습니다.아래 코드를 참조해주시기 바랍니다.

@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;
    }
}

@Anthoneef great answer와 관련하여 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();
    }
}

예외는 여기에 던져집니다(Fragment Activity).

@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);
    }
}

활동의 현재 상태를 표시하는 플래그를 사용하여 이 문제를 해결합니다.제 솔루션은 다음과 같습니다.

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() 또는 popBackStackImediate() 메서드와 충돌하는 경우 다음을 사용하여 수정하십시오.

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

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

제 경우에는 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())
    }

}

코드 앞에 수행 중인 오버라이드에 '슈퍼'를 추가하면 됩니다.

코틀린 확장

fun FragmentManager?.replaceAndAddToBackStack(
    @IdRes containerViewId: Int,
    fragment: () -> Fragment,
    tag: String
) {
    // Find and synchronously remove a fragment with the same tag.
    // The second transaction must start after the first has finished.
    this?.findFragmentByTag(tag)?.let {
        beginTransaction().remove(it).commitNow()
    }
    // Add a fragment.
    this?.beginTransaction()?.run {
        replace(containerViewId, fragment, tag)
        // The next line will add the fragment to a back stack.
        // Remove if not needed.
        // You can use null instead of tag, but tag is needed for popBackStack(), 
        // see https://stackoverflow.com/a/59158254/2914140
        addToBackStack(tag)
    }?.commitAllowingStateLoss()
}

용도:

val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)

getFragmentManager()getChildFragmentManager() 안 ㅠㅠㅠㅠㅠㅠㅠFragmentManager자기 자신을 이용하려고 노력합니다.

당신의 충돌 보고서에서 볼 수 있듯이, 예외를 던지는 마지막 줄입니다.

checkStateLoss(FragmentManager.java:1109)

checkStateLoss 구현을 살펴보면

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

따라서 저에게 간단한 해결책은 앱에서 호출하는 Fragment Manager의 메서드를 찾아 이 메서드를 호출하기 전에 isStateSaved()가 false인지 확인하는 것입니다.저에게 그것은 쇼() 방법이었습니다.이렇게 했어요.

if (!isStateSaved()) {
  myDialog.show(fragmentManager, Tag)
}

24부터는 "24.0.0"으로 할 수 .FragmentTransaction.commitNow() 을 호출하는 입니다.commit()에 뒤에executePendingTransactions()설명서에 나와 있듯이 이 접근 방식은 다음과 같습니다.

commitNow를 호출하는 것이 commit()을 호출한 후 executePendingTransactions()를 호출하는 것보다 더 좋습니다. 후자는 원하는 동작이든 아니든 현재 보류 중인 모든 트랜잭션을 커밋하려고 시도하는 부작용이 있기 때문입니다.

@Ovidiu Latcu가 수락한 답변이 있다는 것을 알고 있지만, 잠시 후에도 오류는 여전히 지속됩니다.

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

크래시리틱스는 여전히 나에게 이상한 오류 메시지를 보냅니다.

하지만 지금은 버전 7+에서만 오류가 발생합니다(Nougat). 나의 수정 사항은 커밋을 사용하는 것이었습니다.fragmentTransaction에서 commit() 대신 StateLoss()허용합니다.

게시물은 커밋에 도움이 됩니다.StateLoss()를 허용하고 다시는 fragment 문제가 발생하지 않습니다.

요약하자면, 여기서 수락된 답변은 Nugat 이전 안드로이드 버전에서 작동할 수 있습니다.

이렇게 하면 검색 시간을 몇 시간 절약할 수 있습니다.해피 코딩만세 삼창

이 문제를 무시하려면 Google I/O 2018에 도입된 Navigation Architecture Component를 사용할 수 있습니다.Navigation Architecture 구성 요소는 Android 앱에서 내비게이션 구현을 단순화합니다.

활동에 추가합니다.

@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.

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

반응형