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

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

26 ответов


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

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

  • удаление вызова super.onSaveInstanceState не поможет.
  • создание фрагмента с commitAllowingStateLoss не поможет.

вот как я наблюдал проблему:

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

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

@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 года, и Google делает библиотеку поддержки Android с новым компонентом жизненного цикла вызовов. Это дает некоторую новую идею для этой проблемы "не может выполнить это действие после onSaveInstanceState".

короче:

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

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

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

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

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

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

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

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

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

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

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

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

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


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

Следуйте Простым Шагам

шаги

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

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

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

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


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


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

public void onBackPressed() {}

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


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

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

если транзакция происходит в ChildFragmentManager parentFragment, используйте 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();
        }

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

изменить, чтобы это решило проблему:

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 (), и приложение работает нормально.Надеюсь, это поможет.


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

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

, например isChangingConfigurations()

то есть

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

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


Если вы делаете некоторую 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;
    }
}

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

popMyFragmentAndMoveOn();

для этого:

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

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


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

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

или

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

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

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

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

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

в отношении @ 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();
    }
}

добавьте это в свою активность

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

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

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


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


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

}


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

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


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

@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+ (Нуга) Мое исправление было использовать commitAllowingStateLoss() вместо commit () в fragmentTransaction.

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

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

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


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

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

Я положил его в onStart это было правильно

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

}

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


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

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

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

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