получение исключения «IllegalStateException: не удается выполнить это действие после onSaveInstanceState»

355

У меня есть приложение Live Android, и с маркета я получил следующую трассировку стека, и я понятия не имею, почему это происходит, потому что это происходит не в коде приложения, а вызвано тем или иным событием из приложения (предположение)

Я не использую фрагменты, но есть ссылка на FragmentManager. Если какое-либо тело может пролить некоторый свет на некоторые скрытые факты, чтобы избежать этого типа проблемы:

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.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
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.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:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
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:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  
dcool
источник
Вы уже нашли решение? Имея ту же проблему здесь: stackoverflow.com/questions/7575921/…
nhaarman
2
@phlebas Нет, ты не сделал. Ваш касается диалогов, а это не так. Недостаточно верхней строки вашего соответствия трассировки стека. В остальном очень разные. Я говорю это, потому что я просто посмотрел на вашу проблему, и это, к сожалению, мне не поможет.
2012 г.
Используете ли вы поток или AsynTask в этой деятельности?
Хосе Кастро
21
Я обсуждаю эту ошибку в своем сообщении в блоге ... вы должны прочитать ее. :)
Алекс Локвуд

Ответы:

455

Это самая глупая ошибка, с которой я когда-либо сталкивался. У меня было Fragmentприложение, отлично работающее для API <11 , и Force Closingдля API> 11 .

Я действительно не мог понять, что они изменили внутри Activityжизненного цикла в вызове saveInstance, но я вот как я решил это:

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

Я просто не звоню .super()и все отлично работает. Надеюсь, это сэкономит вам время.

РЕДАКТИРОВАТЬ: после некоторых дополнительных исследований, это известная ошибка в пакете поддержки.

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

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDIT2: это также может произойти, если вы пытаетесь выполнить транзакцию после того, как ваш Activityушел в фоновом режиме. Чтобы избежать этого, вы должны использоватьcommitAllowingStateLoss()

EDIT3: вышеупомянутые решения исправляли проблемы в ранних библиотеках support.v4 из того, что я помню. Но если у вас все еще есть проблемы с этим, вы ДОЛЖНЫ также прочитать блог @AlexLockwood : Потеря транзакций и активности фрагментов

Резюме из поста в блоге (но я настоятельно рекомендую вам прочитать его):

  • НИКОГДА не проводите commit() транзакции после onPause()до соты и onStop()после соты
  • Будьте осторожны при совершении транзакций внутри Activityметодов жизненного цикла. Использование onCreate() , onResumeFragments()иonPostResume()
  • Избегайте выполнения транзакций внутри методов асинхронного обратного вызова
  • Используйте commitAllowingStateLoss()только в крайнем случае
Овидиу Латку
источник
97
Вы должны использовать commitAllowingStateLoss () вместо commit ()
Мех
7
Поэтому, не вызывая super в onSaveInstanceState, вы не сможете FragmentManager сохранить состояние всех фрагментов и восстановить их. Это может вызвать проблемы с ротацией. Кроме того, я только что попробовал другую вещь, чтобы положить мусор в пакет, и это не имеет значения для меня. Не уверен, как бы это было - ошибка, на которую вы ссылались в пакете поддержки, является исключением NullPointerException, и похоже, что это не похоже на IllegalStateException ...
themightyjon
56
@meh commitAllowingStateLoss()только избегает исключения. Это не защищает ваше приложение от случайной потери состояния. Смотрите этот пост в блоге .
Алекс Локвуд
2
@AlexLockwood, так что из этого поста в блоге мы можем узнать, что мы должны выполнять все наши сетевые вызовы внутри фрагмента (и отображать некоторый временный пользовательский интерфейс прогресса, если это необходимо), и это единственный способ избежать этого исключения, когда оно, вероятно, происходит из-за фиксации. вызывается после некоторого асинхронного вызова метода.
Мех
1
Я не получаю первое замечание: «НИКОГДА не совершайте транзакции () после ... onStop () после сота». Что если мне понадобится кнопка, чтобы вызвать замену фрагмента другим? Должен ли я поставить логическое значение, которое проверяет, завершил ли действие onStop, и если это так, вместо этого вызовите commitAllowingStateLoss? Кроме того, что если у меня есть фрагмент внутри фрагмента, который мне нужно заменить при нажатии кнопки?
разработчик Android
76

Поиск исходного кода Android о причинах этой проблемы показывает, что флаг mStateSaved в FragmentManagerImplклассе (экземпляр доступен в Activity) имеет значение true. Он имеет значение true, когда задний стек сохраняется (saveAllState) при вызове из Activity#onSaveInstanceState. После этого вызовы из ActivityThread не сбрасывают этот флаг, используя доступные методы сброса FragmentManagerImpl#noteStateNotSaved()иdispatch() .

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

Хорошие способы

Прежде всего: я бы рекламировал статью Алекса Локвуда . Тогда из того, что я сделал до сих пор:

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

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

  2. Сразу после того, как транзакция зафиксирована (вы только что звонили commit()), позвоните FragmentManager.executePendingTransactions().

Не рекомендуемые способы:

  1. Как упоминал Овидиу Латку, не звоните super.onSaveInstanceState(). Но это означает, что вы потеряете все состояние своей активности вместе с состоянием фрагментов.

  2. Переопределить onBackPressedи только там вызов finish(). Это должно быть нормально, если ваше приложение не использует Fragments API; как super.onBackPressedтам есть вызов FragmentManager#popBackStackImmediate().

  3. Если вы используете API Fragments и состояние вашей активности важно / важно, то вы можете попытаться позвонить с помощью Reflection API FragmentManagerImpl#noteStateNotSaved(). Но это взлом, или можно сказать, что это обходной путь. Мне это не нравится, но в моем случае это вполне приемлемо, поскольку у меня есть код из унаследованного приложения, в котором используется устаревший код ( TabActivityи неявно LocalActivityManager).

Ниже приведен код, который использует отражение:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

Ура!

Гунар
источник
Похоже, это происходит и с ActionBarSherlock в Gingerbread, поэтому случай проверки Build Id выглядит спорным ... :(
t0mm13b
Кроме того, вы должны указать - это не подходит для использования АБС :)
t0mm13b
@ t0mm13b: приведенный выше код обозначает мой проект, так как он не использует ни фрагменты, ни support.FragmentActivity. Он работает на android.app.Activity, и поскольку несогласованность, которая вызывает исключение, вызвана FragmentManager (API уровня 11 и выше), поэтому проверьте ... Если вы считаете, что источник зла для вас тоже один, не стесняйтесь снять чек. ABS - это отдельная история, поскольку она работает поверх пакета совместимости и реализации support.FragmentActivity может использовать одну и ту же реализацию FragmentManager и voila: та же проблема.
Gunar
@ t0mm13b: чтобы добавить 600 символов недостаточно, вы должны сначала выяснить, что на самом деле вызывает это для вас. Вы также должны понимать, что вышеизложенное - отвратительный взлом, и я не несу никакой ответственности, работает он или нет (для меня было лучшим решением, учитывая обстоятельства). Если вам нужно его использовать, дважды проверьте в исходном коде Compat наличие имен переменных, поскольку они могут отличаться от стандартного пакета. Я надеюсь, что эта проблема будет решена с помощью следующих версий пакета совместимости, но из опыта Android, шансов на это мало ...
gunar
Уххх ... Это точно та же проблема, что и в этом сообщении об ошибке, что является точкой вопроса этого OP. Я поддерживаю мой комментарий - вы должны были явно указать отказ от ответственности и сказать, что это не гарантировано, а также должны были заявить, что вы не использовали фрагменты - иначе зачем писать этот ответ! :) Просто говорю ...
t0mm13b
35

Такое исключение произойдет, если вы попытаетесь выполнить переход фрагмента после вызова вашей активности фрагмента onSaveInstanceState().

Одна из причин, по которой это может произойти, это то, что вы оставляете AsyncTask(или Thread) запущенным, когда действие прекращается.

Любые переходы после onSaveInstanceState() вызова могут быть потеряны, если система восстановит активность для ресурсов и создаст ее позже.

FunkTheMonk
источник
2
Привет, Фанк, у меня есть вопрос, как получилось, что onBackPressed можно вызывать для этого действия или фрагмента, если это действие или фрагмент был остановлен. Кажется, что вышеупомянутое исключение было сгенерировано из некоторого события пользовательского интерфейса (то есть нажатия клавиши BACK), однако я не могу выяснить связь между Async Task и Back.
dcool
Поскольку вы можете сохранить переходы фрагментов в состояние «назад», нажатие кнопки «назад» может привести к обратному переходу, который вы сохранили (поэтому старые фрагменты возвращаются). onSaveInstanceState вызывается до того, как ваша деятельность будет уничтожена, чтобы восстановить ресурсы в системе, а не всегда после вызова onStop. Извините, это не очень понятно в моем ответе.
FunkTheMonk
Funk, но я не использую какие-либо фрагменты в моем приложении. Это могут быть фрагменты, используемые в нативном коде. раньше я думал, что вы говорите о том же.
dcool
1
У меня был AsyncTask со ссылкой на фрагмент. Проблема решена после удаления вызова super () из onSaveInstanceState и замены ссылки из моей AsyncTask на WeakReference <Fragment>.
Буффало
2
@Buffalo Это определенно не решение проблемы. Ты всегда должен звонить super.onSaveInstanceState().
Алекс Локвуд
27

Просто вызовите super.onPostResume () перед показом вашего фрагмента или переместите код в метод onPostResume () после вызова super.onPostResume (). Это решит проблему!

MJ.Ahmadi
источник
6
Вызов onPostResume () гарантирует, что onResumeFragments () будет вызван, и для меня это идеальное решение.
j2emanue
20

Это также может произойти при звонке dismiss() фрагмента диалога после того, как экран был заблокирован \ заглушен, а состояние экземпляра диалога Activity + сохранено. Чтобы обойти этот вызов:

dismissAllowingStateLoss()

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

Брайан Дилли
источник
2
Это была моя точная проблема! Вы, сэр, великолепны!
Таш Пемхива,
17

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

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

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

@Override
public void onSaveInstanceState(Bundle outState) {
};

Шаг 2 : Используйте CommitAllowingStateLoss (); вместо commit (); пока фрагмент операции.

fragmentTransaction.commitAllowingStateLoss();
Basbous
источник
1
Удаление супер метод сделал свое дело, вы можете объяснить, почему, хотя? Это безопасно удалить это?
Брюс
7
удалить super () небезопасно, после этого вы потеряете другие данные conf!
Deadfish
12

Я думаю, что состояние жизненного цикла может помочь предотвратить такой сбой, начиная с поддержки Android lib v26.1.0, вы можете выполнить следующую проверку:

if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)){
  // Do fragment's transaction commit
}

или вы можете попробовать:

Fragment.isStateSaved()

дополнительная информация здесь https://developer.android.com/reference/android/support/v4/app/Fragment.html#isStateSaved ()

Джамал
источник
7

это сработало для меня ... выяснил это самостоятельно ... надеюсь, это поможет вам!

1) НЕ имеет глобальной «статической» FragmentManager / FragmentTransaction.

2) onCreate, ВСЕГДА инициализируйте FragmentManager снова!

образец ниже: -

public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedInstanceState = savedInstanceState;
    setDefaultFragments();
}

protected void setDefaultFragments() {
    fragmentManager = getSupportFragmentManager();
    //check if on orientation change.. do not re-add fragments!
    if(mSavedInstanceState == null) {
        //instantiate the fragment manager

        fragmentTransaction = fragmentManager.beginTransaction();

        //the navigation fragments
        NavigationFragment navFrag = new NavigationFragment();
        ToolbarFragment toolFrag = new ToolbarFragment();

        fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
        fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
        fragmentTransaction.commitAllowingStateLoss();

        //add own fragment to the nav (abstract method)
        setOwnFragment();
    }
}
kurayami88
источник
6

Я всегда получал это, когда пытался показать фрагмент в методе onActivityForResult (), поэтому проблема была в следующем:

  1. Моя активность приостановлена ​​и остановлена, а это значит, что onSaveInstanceState () уже был вызван (для устройств до и сота и после сота).
  2. В случае какого-либо результата я сделал транзакцию, чтобы показать / скрыть фрагмент, который вызывает это IllegalStateException.

Я сделал следующее:

  1. Добавленное значение для определения того, было ли выполнено желаемое действие (например, фотографирование из camere - isPhotoTaken) - это может быть логическое или целочисленное значение, в зависимости от того, сколько разных транзакций вам нужно.
  2. В переопределенном методе onResumeFragments () я проверил свое значение и после того, как мне понадобились транзакции фрагментов. В этом случае commit () не был выполнен после onSaveInstanceState, так как состояние было возвращено в методе onResumeFragments ().
массив
источник
5

Я решил проблему с onconfiguration изменился. Хитрость заключается в том, что в соответствии с жизненным циклом активности андроида, когда вы явно называете намерение (намерение камеры или любое другое); в этом случае действие приостанавливается и вызывается onsavedInstance. При повороте устройства в другое положение, отличное от того, во время которого активность была активной; выполнение операций фрагмента, таких как фиксация фрагмента, вызывает недопустимое исключение состояния. Есть много жалоб на это. Это что-то про управление жизненным циклом активности андроида и правильные вызовы методов. Чтобы решить эту проблему, я сделал это: 1-Переопределите метод onsavedInstance своей деятельности и определите текущую ориентацию экрана (книжную или альбомную), а затем установите ориентацию экрана до того, как ваша деятельность будет приостановлена. таким образом, действие блокирует поворот экрана для вашего действия, если оно было повернуто другим. 2-затем, переопределите метод действия onresume и установите режим ориентации сейчас на датчик, чтобы после вызова onsaved-метода он вызывал еще раз onconfiguration для правильной обработки вращения.

Вы можете скопировать / вставить этот код в свою деятельность, чтобы справиться с ним:

@Override
protected void onSaveInstanceState(Bundle outState) {       
    super.onSaveInstanceState(outState);

    Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
    int orientation =this.getDisplayOrientation();
    //Lock the screen orientation to the current display orientation : Landscape or Potrait
    this.setRequestedOrientation(orientation);
}

//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device 
public int getDisplayOrientation() {
    Display getOrient = getWindowManager().getDefaultDisplay();

    int orientation = getOrient.getOrientation();

    // Sometimes you may get undefined orientation Value is 0
    // simple logic solves the problem compare the screen
    // X,Y Co-ordinates and determine the Orientation in such cases
    if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        Configuration config = getResources().getConfiguration();
        orientation = config.orientation;

        if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        // if height and widht of screen are equal then
        // it is square orientation
            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else { //if widht is less than height than it is portrait
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else { // if it is not any of the above it will defineitly be landscape
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        }
    }
    return orientation; // return value 1 is portrait and 2 is Landscape Mode
}

@Override
public void onResume() {
    super.onResume();
    Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} 
douggynix
источник
4

У меня была такая же проблема, получение IllegalStateException, но замена всех моих вызовов commit () на commitAllowingStateLoss () не помогла.

Виновником был вызов DialogFragment.show ().

Я окружаю это

try {
    dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
    return;
}

и это сделало это. ОК, я не могу показать диалог, но в этом случае все было в порядке.

Это было единственное место в моем приложении, где я сначала вызвал FragmentManager.beginTransaction (), но никогда не вызывал commit (), поэтому я не нашел его, когда искал «commit ()».

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

Роджер К.С. Вернерссон
источник
3
Тоже самое. Я решил переопределить метод 'show (FragmentManager manager, String tag)', заменив 'commit' на 'commitAllowingStateLoss'; потерять что-то, потому что я не могу установить два закрытых атрибута диалога: mDismissed и mShownByMe. Но, похоже, работает каждый раз :)
Франческо Дитрани
Я сделал альтернативное решение для DialogFragment, которое может избежать этого исключения: github.com/AndroidDeveloperLB/DialogShard
разработчик Android
4

Мое решение этой проблемы было

Во фрагмент добавьте методы:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
    guideMap = guideMapFragment.getMap();
    ...
}

@Override
public void onDestroyView() {
    SherlockFragmentActivity a = getSherlockActivity();
    if (a != null && guideMapFragment != null) {
        try {
            Log.i(LOGTAG, "Removing map fragment");
            a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
            guideMapFragment = null;
        } catch(IllegalStateException e) {
            Log.i(LOGTAG, "IllegalStateException on exit");
        }
    }
    super.onDestroyView();
}

Может быть плохо, но не мог найти ничего лучше.

mc.dev
источник
Правда ... перехват исключения может предотвратить сбой приложения, но поведение приводит к тому, что такие фрагменты остаются на экране или не добавляются.
Маркос Васконселос
4
продолжайте прокручивать правда там
анил
4

Я получил эту проблему. Но я думаю, что эта проблема не связана с commit и commitAllowStateLoss.

Следующее сообщение трассировки и исключения стека относится к commit ().

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

Но это исключение было вызвано onBackPressed ()

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)

Все они были вызваны checkStateLoss ()

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }

mStateSaved будет true после onSaveInstanceState.

Эта проблема встречается редко. Я никогда не сталкивался с этой проблемой. Я не могу повторить проблему.

я нашел вопрос 25517

Это могло произойти при следующих обстоятельствах

  1. Клавиша Back вызывается после onSaveInstanceState, но до начала нового действия.

  2. используйте onStop () в коде

Я не уверен, в чем корень проблемы. Поэтому я использовал некрасивый способ.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}
oO_ox
источник
Я действительно не решил проблему, но эта проблема не связана с commit и commitAllowStateLoss.
oO_ox
4

У меня та же проблема в моем приложении. Я решил эту проблему, просто вызвав super.onBackPressed();предыдущий класс и вызвав commitAllowingStateLoss()текущий класс с этим фрагментом.

Питер
источник
2
Спасибо. Это решение решило проблему commitAllowingStateLoss()вместоcommit()
Чинтак Патель
Избегайте использования commitAllowingStateLoss () medium.com/@elye.project/...
swooby
3

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

Возможно, что этот пользователь повернул экран, а затем нажал кнопку «Назад» (потому что также возможно, что этот пользователь шарил по телефону во время использования вашего приложения)

DOLS
источник
2
Хотя изменения конфигурации (например, изменения ориентации) могут привести к этому исключению, они не являются основной причиной.
Алекс Локвуд
2

Та же проблема от меня, и после однодневного анализа всех статей, блогов и стекового потока я нашел простое решение. Ни в коем случае не используйте saveInstanceState, это условие с одной строкой кода. По коду фрагмента:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....
Тобия Канески
источник
2

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

Вы можете решить это двумя способами

Для загрузки фрагмента вы можете использовать транзакциюmit.mitAllowingStateLoss () вместо транзакции транзакции (), но в итоге вы можете потерять выполненную операцию фиксации.

или

Убедитесь, что при загрузке фрагмента активность возобновлена ​​и она не собирается приостанавливаться. Создайте логическое значение и проверьте, не переходит ли действие в состояние onPause ().

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

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

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

if(mIsResumed){
 //load the fragment
}
Шарат Кумар
источник
1

Спасибо @gunar, но я думаю, что есть лучший способ.

Согласно доку:

 * If you are committing a single transaction that does not modify the
 * fragment back stack, strongly consider using
 * {@link FragmentTransaction#commitNow()} instead. This can help avoid
 * unwanted side effects when other code in your app has pending committed
 * transactions that expect different timing.
 *
 * @return Returns true if there were any pending transactions to be
 * executed.
 */
public abstract boolean executePendingTransactions();

Так что используйте commitNowдля замены:

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()
JianxinLi
источник
0

Хорошо, после попытки всех вышеупомянутых решений без успеха (потому что в основном у меня нет транзакций).

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

Я нашел обходной путь, смешивающий много похожих постов:

Это трехэтапное решение, все сделано на вашей FragmentActivity (в данном случае его называют GenericActivity):

private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    //To avoid bug for fragments: Step 2 of 3
    activity = new WeakReference<GenericActivity>(this);
}

@Override
public FragmentManager getSupportFragmentManager(){
    //To avoid bug for fragments: Step 3 of 3
    if (this == activity.get()) {
        return super.getSupportFragmentManager();
    }
    return activity.get().getSupportFragmentManager();
}
Антилопа
источник
0

Когда я использую стартовую активность в одном фрагменте, я получу это исключение;

Когда я перехожу на использование startactivityforresult, исключение исчезает :)

Поэтому простой способ исправить это - использовать API startActivityForResult :)

Вопрос
источник
0

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

DCS
источник
0

Это исправлено в Android 4.2, а также в источнике библиотеки поддержки. [*]

Подробнее о причине (и способах ее устранения) см. В отчете об ошибках Google: http://code.google.com/p/android/issues/detail?id=19917.

Если вы используете библиотеку поддержки, вам не нужно беспокоиться об этой ошибке (надолго) [*]. Однако, если вы используете API напрямую (т.е. не используете FragmentManager библиотеки поддержки) и нацеливаетесь на API ниже Android 4.2, вам придется попробовать один из обходных путей.

[*] На момент написания Android SDK Manager все еще распространял старую версию с этой ошибкой.

Редактировать Я собираюсь добавить некоторые пояснения здесь, потому что я, очевидно, каким-то образом запутал того, кто отрицал этот ответ.

Есть несколько различных (но связанных) обстоятельств, которые могут вызвать это исключение . Мой ответ выше относится к конкретному случаю, обсуждаемому в вопросе, т.е. к ошибке в Android, которая впоследствии была исправлена. Если вы получаете это исключение по другой причине, это потому, что вы добавляете / удаляете фрагменты, когда этого не должно быть (после того, как состояния фрагментов были сохранены). Если вы находитесь в такой ситуации, то вам может пригодиться « Вложенные фрагменты - IllegalStateException« Не удается выполнить это действие после onSaveInstanceState » ».

Бенджамин Добелл
источник
0

Мой вариант использования: я использовал слушателя во фрагменте, чтобы уведомить активность, что что-то произошло. Я сделал новый фрагмент коммита на методе обратного вызова. Это отлично работает в первый раз. Но при изменении ориентации активность воссоздается с сохраненным состоянием экземпляра. В этом случае фрагмент не создается снова означает, что фрагмент имеет прослушиватель, который является старой уничтоженной активностью. В любом случае метод обратного вызова будет запущен в действии. Это идет к разрушенной деятельности, которая вызывает проблему. Решение состоит в том, чтобы сбросить слушателя во фрагменте с текущей активностью. Это решит проблему.

Ганеш Канна
источник
0

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

Крис
источник
0

В моем случае, с тем же исключением ошибки, я поместил onBackPressed () в runnable (вы можете использовать любой вид):

myView.post(new Runnable() {
                    @Override
                    public void run() {
                        onBackPressed()
                    }
                });

Я не понимаю почему, но это работает!

Alecs
источник
Публикация в представлении запускает Runnable только после того, как представление было правильно выложено и выведено на экран; это часто означает, что само действие полностью возобновилось, следовательно, никаких проблем
Mercato
0

Вы можете вызывать фрагментManager.popBackStackImmediate (); когда активность приостановлена. Деятельность не завершена, но приостановлена ​​и не на переднем плане. Вам нужно проверить, приостановлена ​​ли активность перед popBackStackImmediate ().

Мурат
источник
0

Я заметил кое-что очень интересное. У меня в приложении есть возможность открыть галерею телефона, и устройство спрашивает, какое приложение использовать, там я нажимаю на серую область вдали от диалогового окна и вижу эту проблему. Я заметил, как моя деятельность переходит от onPause, onSaveInstanceState обратно к onResume, не происходит посещение onCreateView. Я делаю транзакции в onResume. Итак, что я в итоге сделал, так это установил флаг, отменяющий onPause, но являющийся истинным onCreateView. если флаг равен true onResume, тогда делайте onCommit, иначе commitAllowingStateLoss. Я мог бы продолжать и тратить столько времени, но я хотел проверить жизненный цикл. У меня есть устройство, которое является sdkversion 23, и я не получаю эту проблему, но у меня есть другое, которое является 21, и там я вижу его.

Хуан Мендес
источник
-1

вы можете использовать FragmentActivity.onStart перед popBackStackImmediate

нравится:

public void backStackFragment() {
    this.start();
    getFragmentManager().popBackStackImmediate();
}

public void start(){
    FragmentActivity a = getActivity();
    if(a instanceof DepositPlanPadActivity){
      ((DepositPlanPadActivity)a).onStart();
    }
    if(a instanceof SmallChangePlanPad){
            ((SmallChangePlanPad)a).onStart();
        }
        if(a instanceof UserCenterActivity){
            ((UserCenterActivity)a).onStart();
        }
    }

http://jorryliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

user3327339
источник