IllegalStateException: не может выполнить это действие после onSaveInstanceState с ViewPager - PullRequest
445 голосов
/ 28 сентября 2011

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

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

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

Ответы [ 32 ]

3 голосов
/ 14 июля 2014

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

2 голосов
/ 11 апреля 2018

Если вы выполняете какую-либо 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 голосов
/ 01 декабря 2018

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

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

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

2 голосов
/ 13 августа 2015

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

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

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

т.е.

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

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

1 голос
/ 02 июня 2017

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

popMyFragmentAndMoveOn();

на это:

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

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

1 голос
/ 13 сентября 2017

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

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

или

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

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

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

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

if(mIsResumed){
//load the your fragment
}
1 голос
/ 17 июля 2018

Что касается @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 голос
/ 24 января 2019

В моем случае я получил эту ошибку в методе переопределения 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 голос
/ 22 июня 2018

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

1 голос
/ 30 июня 2018

Предоставлено: Решение для 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;
 }
}
...