IllegalStateException: не удается выполнить это действие после onSaveInstanceState с помощью 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)

По-видимому, это имеет какое-то отношение к FragmentManager, который я не использую. В стеке не отображается ни один из моих собственных классов, поэтому я понятия не имею, где это исключение возникает и как его предотвратить.

Для записи: у меня есть табуст, и на каждой вкладке происходит переключение ActivityGroup между действиями.

Ответ 1

Пожалуйста, проверьте мой ответ здесь. В основном я просто должен был:

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

Ответ 2

Есть много связанных проблем с похожим сообщением об ошибке. Проверьте вторую строку этой конкретной трассировки стека. Это исключение специально связано с вызовом FragmentManagerImpl.popBackStackImmediate.

Этот вызов метода, как и popBackStack, всегда завершится ошибкой с IllegalStateException если состояние сеанса уже было сохранено. Проверьте источник. Вы ничего не можете сделать, чтобы остановить это исключение.

  • Удаление вызова super.onSaveInstanceState не поможет.
  • Создание фрагмента с помощью commitAllowingStateLoss не поможет.

Вот как я заметил проблему:

  • Там форма с кнопкой отправки.
  • При нажатии кнопки создается диалоговое окно и запускается асинхронный процесс.
  • Пользователь нажимает клавишу home до завершения процесса - onSaveInstanceState.
  • Процесс завершается, выполняется обратный вызов и popBackStackImmediate попытка popBackStackImmediate.
  • IllegalStateException выбрасывается.

Вот что я сделал, чтобы решить это:

Поскольку избежать обратного вызова IllegalStateException в IllegalStateException, перехватывайте и игнорируйте его.

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There 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;
    }
}

Ответ 3

Проверьте, не активна ли операция 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();
}

Ответ 4

Вот другое решение этой проблемы.

Используя переменную частного члена, вы можете установить возвращаемые данные в качестве намерения, которые затем могут быть обработаны после 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;
    }
}

Ответ 5

В октябре 2017 года Google создает библиотеку поддержки Android с новым компонентом Lifecycle. Это дает новую идею для этой проблемы "Не удается выполнить это действие после onSaveInstanceState".

Короче:

  • Используйте компонент жизненного цикла, чтобы определить, правильное ли время всплывающего фрагмента.

Более длинная версия с объяснением:

  • почему эта проблема выходит?

    Это потому, что вы пытаетесь использовать FragmentManager из своей деятельности (которая будет содержать ваш фрагмент, я полагаю?) Для фиксации транзакции для вашего фрагмента. Обычно это выглядит так, как будто вы пытаетесь выполнить какую-то транзакцию для предстоящего фрагмента, в то время как действия хоста уже вызывают метод savedInstanceState (может случиться, что пользователь прикоснется к кнопке home, поэтому действие вызывает onStop(), в моем случае это причина)

    Обычно такой проблемы не должно быть - мы всегда пытаемся загрузить фрагмент в действие в самом начале, так как метод onCreate() является идеальным местом для этого. Но иногда это случается, особенно когда вы не можете решить, какой фрагмент вы будете загружать для этого действия, или вы пытаетесь загрузить фрагмент из блока AsyncTask (или что-то займет немного времени). Время до того, как транзакция фрагмента действительно произойдет, но после действия onCreate() пользователь может сделать что угодно. Если пользователь onSavedInstanceState() кнопку "Домой", которая активирует метод onSavedInstanceState(), возникнет сбой при can not perform this action.

    Если кто-то хочет глубже в этом вопросе, я предлагаю им взглянуть на этот пост в блоге. Он смотрит глубоко в слой исходного кода и многое объясняет по этому поводу. Кроме того, это дает причину того, что вы не должны использовать метод commitAllowingStateLoss() чтобы обойти этот сбой (поверьте мне, он не предлагает ничего хорошего для вашего кода)

  • Как это исправить?

    • Должен ли я использовать commitAllowingStateLoss() для загрузки фрагмента? Нет, ты не должен;

    • Должен ли я переопределить метод onSaveInstanceState, игнорировать метод super внутри него? Нет, ты не должен;

    • Должен ли я использовать магическую внутреннюю активность isFinishing, чтобы проверить, находится ли активность хоста в нужный момент для транзакции фрагмента? Да, это похоже на правильный способ сделать.

  • Посмотрите, что может сделать компонент жизненного цикла.

    По сути, Google делает некоторую реализацию внутри класса AppCompatActivity (и нескольких других базовых классов, которые вы должны использовать в своем проекте), что упрощает определение текущего состояния жизненного цикла. Вспомните нашу проблему: почему эта проблема возникла? Это потому, что мы делаем что-то не в то время. Поэтому мы стараемся этого не делать, и эта проблема исчезнет.

    Я немного кодирую для своего собственного проекта, вот что я делаю, используя LifeCycle. Я пишу код в Котлине.

val hostActivity: AppCompatActivity? = null // the activity to host fragments. It 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, эта строка кода не будет работать и, следовательно, ничего не будет отображаться на вашем экране. Когда пользователи возвращаются в приложение, они видят пустой экран, на котором на активности пустого хоста вообще нет фрагментов. Это плохой опыт (да, немного лучше, чем крушение).

    Поэтому здесь я хотел бы, чтобы могло быть что-то более приятное: приложение не будет аварийно onResume, если оно переходит в состояние жизни позже, чем onResume, метод транзакции 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) аннотации. Теперь я думаю, что это должно работать так, как я ожидал.

Ответ 6

Краткое и рабочее решение:

Следуйте простым шагам

Шаги

Шаг 1: переопределить состояние onSaveInstanceState в соответствующем фрагменте. И удалите из него супер-метод.

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

Шаг 2: Используйте fragmentTransaction.commitAllowingStateLoss( );

вместо fragmentTransaction.commit( ); в то время как операции фрагмента.

Ответ 7

BEWARE, использование transaction.commitAllowingStateLoss() может привести к плохому опыту для пользователя. Для получения дополнительной информации о том, почему это исключение выбрано, см. этот пост.

Ответ 8

Я нашел грязное решение для этой проблемы. Если вы по-прежнему хотите сохранить свой ActivityGroups по какой-либо причине (у меня были причины ограничения по времени), вы просто реализуете

public void onBackPressed() {}

в вашем Activity и выполните код back. даже если такого метода для старых устройств нет, этот метод вызывается более новыми.

Ответ 9

Не используйте commitAllowingStateLoss(), его следует использовать только в тех случаях, когда это нормально, когда состояние пользовательского интерфейса неожиданно меняется для пользователя.

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

Если транзакция происходит в ChildFragmentManager of parentFragment, используйте для проверки parentFragment.isResume() снаружи.

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

Ответ 10

У меня была аналогичная проблема, сценарий был примерно таким:

  • Моя активность добавляет/заменяет фрагменты списка.
  • Каждый фрагмент списка имеет ссылку на действие, чтобы уведомлять о деятельности при щелчке элемента списка (шаблон наблюдателя).
  • Каждый фрагмент списка вызывает 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);

вам нужно настроить своих слушателей каждый раз, когда создается действие, чтобы избежать ситуации, когда фрагменты ссылаются на старые уничтоженные экземпляры активности.

Ответ 11

Я получал это исключение, когда я нажимал кнопку "Назад", чтобы отменить намерение выбора на моей активности фрагмента карты. Я решил это, заменив код onResume (где я инициализировал фрагмент) на onstart(), и приложение работает нормально. Надеюсь, что это поможет.

Ответ 12

Я думаю, что использование transaction.commitAllowingStateLoss(); не лучшее решение. Это исключение будет выбрано при изменении конфигурации активности и вызове фрагмента onSavedInstanceState(), после чего ваш метод обратного вызова async попытается зафиксировать фрагмент.

Простое решение может быть проверено, меняется ли активность конфигурации или нет.

например. проверьте isChangingConfigurations()

то есть.

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

Оформить покупку эту ссылку

Ответ 13

Если вы выполняете какую-то FragmentTransaction в onActivityResult, вы можете установить некоторое логическое значение внутри 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;
    }
}

Ответ 14

Если вы наследуете от FragmentActivity, вы должны вызвать суперкласс в onActivityResult():

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

Если вы этого не сделаете и попытаетесь показать диалоговое окно фрагмента в этом методе, вы можете получить OP IllegalStateException. (Если честно, я не совсем понимаю, почему супер вызов решает проблему. onActivityResult() вызывается перед onResume(), поэтому ему все равно нельзя показывать диалоговое окно фрагмента.)

Ответ 15

Начиная с версии поддержки версии 24.0.0, вы можете вызвать метод FragmentTransaction.commitNow(), который совершает транзакцию синхронно вместо вызова commit(), а затем executePendingTransactions(). Поскольку документация говорит, что этот подход еще лучше:

Вызов commitNow предпочтительнее вызова commit(), за которым следует executePendingTransactions(), поскольку последний будет иметь побочный эффект от попыток зафиксировать все текущие ожидающие транзакции, является ли это желаемым поведением или нет.

Ответ 16

Возможно, самое гладкое и самое простое решение, которое я нашел в моем случае, заключалось в том, чтобы не вызывать оскорбительный фрагмент из стека в ответ на результат активности. Поэтому, изменив этот вызов в моем onActivityResult():

popMyFragmentAndMoveOn();

:

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

помог в моем случае.

Ответ 17

Всякий раз, когда вы пытаетесь загрузить фрагмент в своей деятельности, убедитесь, что активность возобновлена ​​и не будет приостановлена. В состоянии паузы вы можете потерять завершающую операцию фиксации.

Вы можете использовать transaction.commitAllowingStateLoss() вместо transaction.commit() для загрузки фрагмента

или

Создайте логическое значение и проверьте, не активируется ли действие onpause

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

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

тогда при загрузке проверки фрагмента

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

Ответ 18

Чтобы обойти эту проблему, мы можем использовать Компонент архитектуры навигации, который был представлен в Google I/O 2018. Компонент архитектуры навигации упрощает реализацию навигации в приложении Android.

Ответ 19

Вежливость: решение для IllegalStateException

Эта проблема раздражала меня много времени, но, к счастью, я нашел для нее конкретное решение. Подробное объяснение этого здесь.

Использование commitAllowStateloss() может предотвратить это исключение, но приведет к нарушениям пользовательского интерфейса. До сих пор мы понимали, что IllegalStateException встречается, когда мы пытаемся зафиксировать фрагмент после состояния Activity lost-, поэтому мы должны просто отложить транзакцию до состояния восстановлен. Это можно сделать просто так

Объявите две приватные логические переменные

 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. Идея состоит в том, чтобы помечать транзакции как безопасные только тогда, когда активность находится на переднем плане, поэтому нет шансов потерять состояние.

/*
onPostResume is called only when the activity 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();
    }
}

-What, который мы сделали до сих пор, спасет от 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;
 }
}

Ответ 20

Что касается @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();
    }
}

Ответ 21

В моем случае я получил эту ошибку в методе переопределения 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())
    }

}

Возможно, вам просто нужно добавить super в любое переопределение, которое вы делаете перед вашим кодом.

Ответ 22

Добавьте это в свою деятельность

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

Ответ 23

Я также столкнулся с этой проблемой, и проблема возникает каждый раз, когда изменяется контекст вашего FragmentActivity (например, изменение ориентации экрана и т.д.). Поэтому лучшим решением для этого является обновление контекста из вашего FragmentActivity.

Ответ 24

Исключение выдается здесь (в 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);
    }
}

Я решаю эту проблему, просто используя флаг, чтобы отметить текущий статус активности. Вот мое решение:

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() throwing IllegalStateException
            super.onBackPressed();
        }
    }
}

Ответ 25

Я закончил с созданием базового фрагмента и сделать все фрагменты в моем приложении расширить его

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

Ответ 26

Другой возможный обходной путь, который я не уверен, помогает ли во всех случаях (происхождение здесь):

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

Ответ 27

Я знаю, что @Ovidiu Latcu принял приемлемый ответ, но через некоторое время ошибка все еще сохраняется.

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

Crashlytics все еще посылает мне это странное сообщение об ошибке.

Однако ошибка теперь возникает только в версии 7+ (Nougat). Мое исправление состояло в том, чтобы использовать commitAllowingStateLoss() вместо commit() для фрагмента транзакции.

Этот пост полезен для commitAllowingStateLoss() и никогда больше не сталкивался с проблемой фрагментов.

Подводя итог, можно сказать, что принятый ответ может работать на версиях для Android до Nougat.

Это может сэкономить кому-то несколько часов поиска. счастливых кодировок. <3 ура

Ответ 28

У меня была точно такая же проблема. Это произошло из-за разрушения предыдущей деятельности. когда я поддержал предыдущую деятельность, она была уничтожена. Я поставил базовую активность (НЕПРАВИЛЬНО)

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

Я положил это в начало, это было ПРАВО

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

}

Ответ 29

Если у вас произошел сбой с помощью метода popBackStack() или popBackStackImmediate(), попробуйте исправить с помощью:

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

Это работает и для меня.

Ответ 30

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