it-swarm.com.ru

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

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

414
nhaarman

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

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

Не вызывайте super() для метода saveInstanceState. Это все испортило ...

Это известная ошибка в пакете поддержки. 

Если вам нужно сохранить экземпляр и добавить что-то в вашу outStateBundle, вы можете использовать следующее:

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

656
Ovidiu Latcu

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

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

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

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

  • Есть форма с кнопкой отправки.
  • При нажатии кнопки создается диалоговое окно и запускается асинхронный процесс.
  • Пользователь нажимает клавишу home до завершения процесса - вызывается onSaveInstanceState.
  • Процесс завершается, выполняется обратный вызов и предпринимается попытка 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;
    }
}
108
Synesso

Прежде чем показывать фрагмент, проверьте, является ли действие 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();
}
51
Naskov

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

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

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

Короче:

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

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

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

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

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

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

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

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

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

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

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

    По сути, Google делает некоторую реализацию внутри класса 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, эта строка кода не будет работать и, следовательно, ничего не будет отображаться на вашем экране. Когда пользователи возвращаются в приложение, они видят пустой экран, то есть пустое действие «Хост», на котором нет фрагментов вообще. Это плохой опыт (да, немного лучше, чем крушение).

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

20
Anthonyeef

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

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

Меры

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

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

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

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

15
Vinayak

БУДЬТЕ ОСТОРОЖНЫ, использование transaction.commitAllowingStateLoss() может привести к плохой работе для пользователя. Для получения дополнительной информации о том, почему выбрасывается это исключение, см. этот пост .

12
Eric Brandwein

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

public void onBackPressed() {}

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

10
saberrider

Не используйте 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);
}
6
Chandler

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

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

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

5
Mina Samy

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

3
DCS

Если вы выполняете какую-либо 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;
    }
}
2
anoopbryan2

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

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

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

то есть.

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

Оформить заказ это ссылка также

2
Amol Desai

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

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

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

1
Lawrence Kesteloot

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

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

popMyFragmentAndMoveOn();

к этому:

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

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

1
mojuba

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

1
Levon Petrosyan

В моем случае я получил эту ошибку в методе переопределения onActivityResult. После копания я просто выясняю, возможно, мне нужно было позвонить ' super ' раньше.
Я добавил это, и это просто сработало

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' к любому переопределению, которое вы делаете перед вашим кодом.

1
Gian Gomen

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

Вы можете использовать транзакциюmit.comlAllowingStateLoss () вместо транзакции

или же

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

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

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

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

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

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

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

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

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

 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'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;
 }
}
1
IrshadKumail

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

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

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

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

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

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

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

0
ralphgabb

используйте remove () вместо popup (), если состояние сохранено.

   private void removeFragment() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    if (fragmentManager.isStateSaved()) {
        List<Fragment> fragments = fragmentManager.getFragments();
        if (fragments != null && !fragments.isEmpty()) {
            fragmentManager.beginTransaction().remove(fragments.get(fragments.size() - 1)).commitAllowingStateLoss();
        }
    }
}
0
kim

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

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

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

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

}
0
Samet öztoprak

Еще один возможный обходной путь, который, я не уверен, поможет ли во всех случаях (Origin 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();
        }
    }
}
0
android developer

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

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

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

0
Tapa Save

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

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

0
Ahmad El-Melegy

Исключение выдается здесь (в 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()'s throwing IllegalStateException
            super.onBackPressed();
        }
    }
}
0
Frost Lau

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

0
no_cola

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

0
Adam

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

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

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

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

0
Volodymyr Khodonovych