IllegalStateException при изменении конфигурации, если вызов DialogFragment в runnable [duplicate]

Нет. В настоящее время на консоли видны только сообщения, отправленные через консоль уведомлений Firebase. Сообщения, отправленные через API, можно отслеживать в Diagnostics Tool , имея в виду, что это не включает сообщения, отправленные по темам .

398
задан rds 19 November 2015 в 10:31
поделиться

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.

624
ответ дан Community 21 August 2018 в 15:22
поделиться
  • 1
    Вы должны использовать commitAllowingStateLoss () вместо commit () – meh 28 September 2012 в 10:44
  • 2
    Этот комментарий о commitAllowingStateLoss () является ответом в своем собственном праве - вы должны опубликовать его как таковой. – Risadinha 15 October 2013 в 18:59
  • 3
    Что касается 'commitAllowingStateLoss' - / & gt; «Это опасно, поскольку фиксация может быть потеряна, если действие необходимо восстановить позже из состояния, поэтому это должно использоваться только в тех случаях, когда состояние пользовательского интерфейса может неожиданно изменяться на пользователя». – Codeversed 17 December 2013 в 04:44
  • 4
    Если я посмотрю на источник v4 для popBackStackImmediate, он немедленно провалится, если состояние сохранено. Ранее добавление фрагмента с commitAllowingStateLoss не играет никакой роли. Мое тестирование показывает, что это правда. Это не влияет на это конкретное исключение. Нам нужен метод popBackStackImmediateAllowingStateLoss. – Synesso 9 January 2015 в 02:32
  • 5
    @DanieleB да, я написал ответ здесь. Но на самом деле я нашел еще лучшее решение, используя шину сообщений Otto: зарегистрируйте фрагмент в качестве подписчика и выслушайте результат асинхронной передачи. Отменить регистрацию на паузу и перерегистрироваться в резюме. Async также нуждается в методе Produce для времени, когда он завершается, и фрагмент приостановлен. Когда я получу время, я уточню свой ответ с этим более подробно. – Synesso 8 March 2015 в 22:25

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

public class BaseFragment extends Fragment {

    private boolean mStateSaved;

    @CallSuper
    @Override
    public void onSaveInstanceState(Bundle outState) {
        mStateSaved = true;
        super.onSaveInstanceState(outState);
    }

    /**
     * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
     * would otherwise occur.
     */
    public void showAllowingStateLoss(FragmentManager manager, String tag) {
        // API 26 added this convenient method
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager.isStateSaved()) {
                return;
            }
        }

        if (mStateSaved) {
            return;
        }

        show(manager, tag);
    }
}

Тогда, когда я пытаюсь показать фрагмент, я использую showAllowingStateLoss вместо show

следующим образом:

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

Я подошел к этому решению из этого PR: https://github.com/googlesamples/easypermissions/pull/170/files

0
ответ дан Ahmad El-Melegy 21 August 2018 в 15:22
поделиться

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

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

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

i.e.

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

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

2
ответ дан Amol Desai 21 August 2018 в 15:22
поделиться
  • 1
    Как-то я получил это исключение, когда пользователь нажимает на что-то (щелчок - это триггер для совершения транзакции). Как это могло произойти? Ваше решение здесь здесь? – android developer 21 September 2015 в 11:24
  • 2
    @androiddeveloper, что еще вы делаете при нажатии на пользователя. так или иначе фрагмент сохраняет свое состояние до совершения транзакции – Amol Desai 23 September 2015 в 11:24
  • 3
    Исключение было выбрано на точной строке фиксации транзакции. Кроме того, у меня была странная опечатка: вместо «здесь здесь». Я имел в виду «работать здесь». , – android developer 23 September 2015 в 20:29
  • 4
    @androiddeveloper вы правы! но прежде чем совершать транзакции, вы создаете какой-либо фоновый поток или что-то еще? – Amol Desai 24 September 2015 в 07:11
  • 5
    Я так не думаю (извините, что я вне офиса), но почему это имеет значение? Здесь все UI-файлы ... Если я когда-нибудь сделаю что-то в фоновом потоке, у меня бы остались исключения, плюс я не помещал связанный с UI материал в фоновом потоке, поскольку он слишком рискован. – android developer 24 September 2015 в 14:13

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

@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 21 August 2018 в 15:22
поделиться

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

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

или

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

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

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

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

if(mIsResumed){
//load the your fragment
}
1
ответ дан Anonymous 21 August 2018 в 15:22
поделиться

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

Короче:

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

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

  • почему эта проблема возникает? Это потому, что вы пытаетесь использовать FragmentManager из своей активности (которая, как предполагается, держит ваш фрагмент, предположим?), Чтобы зафиксировать транзакцию для фрагмента. Обычно это будет выглядеть так, как будто вы пытаетесь выполнить некоторую транзакцию для следующего фрагмента, в то время как активность хоста уже вызывает метод savedInstanceState (пользователь может попасть в домашнюю кнопку, чтобы активность вызывала onStop(), в моем случае это Причина) Обычно эта проблема не должна происходить - мы всегда пытаемся загрузить фрагмент в действие в самом начале, как метод onCreate() - это идеальное место для этого. Но иногда это случается, особенно когда вы не можете решить, какой фрагмент вы загрузите для этой активности, или вы пытаетесь загрузить фрагмент из блока AsyncTask (или что-то займет немного времени). Время, прежде чем транзакция фрагмента действительно произойдет, но после метода onCreate() активности пользователь может сделать что угодно. Если пользователь нажимает кнопку «домой», которая запускает метод onSavedInstanceState() активности, произошел сбой can not perform this action. Если кто-то захочет увидеть глубже в этом вопросе, я предлагаю им взглянуть на этот блог post . Он выглядит глубоко внутри исходного кода и объясняет многое об этом. Кроме того, это указывает на то, что вы не должны использовать метод commitAllowingStateLoss() для обхода этого сбоя (верьте мне, что он не предлагает ничего хорошего для вашего кода)
  • Как это исправить? Должен ли я использовать метод commitAllowingStateLoss() для загрузки фрагмента? Нет, не стоит; Должен ли я переопределить метод onSaveInstanceState, игнорировать метод super внутри него? Нет, не стоит; Должен ли я использовать магическую активность isFinishing для проверки активности хоста в нужный момент для транзакции фрагмента? Да, это похоже на правильный путь.
  • Посмотрите, что может сделать компонент Lifecycle . В основном, 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()
    }

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

16
ответ дан Anthonyeef 21 August 2018 в 15:22
поделиться

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

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (outState.isEmpty()) {
        // Work-around for a pre-Android 4.2 bug
        outState.putBoolean("bug:fix", true);
    }
}
0
ответ дан Balchander V R 21 August 2018 в 15:22
поделиться

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

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

Вместо этого используйте if (fragment.isResume ()) для проверки вне операции, с которой вы столкнулись с этим IllegalStateException "Can не выполнять это действие после onSaveInstanceState "

5
ответ дан Chandler 21 August 2018 в 15:22
поделиться

Если вы делаете некоторое 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;
    }
}
1
ответ дан Daniel 21 August 2018 в 15:22
поделиться
  • 1
    Пожалуйста, не отправляйте код в качестве изображения, вместо этого используйте форматирование кода. – Micer 11 April 2018 в 12:33
  • 2
    – sandhya sasane 16 September 2018 в 15:24

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

3
ответ дан DCS 21 August 2018 в 15:22
поделиться

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

11
ответ дан Eric Brandwein 21 August 2018 в 15:22
поделиться
  • 1
    Это не дает ответа на вопрос, вы должны предоставить действительный ответ на вопрос – Umar Ata 13 February 2018 в 13:32

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

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

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 21 August 2018 в 15:22
поделиться

Предоставлено: Решение для 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. Идея заключается в том, чтобы пометить безопасность транзакций только тогда, когда активность находится на переднем плане, поэтому нет никаких шансов 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;
 }
}
0
ответ дан IrshadKumail 21 August 2018 в 15:22
поделиться

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

Используя частную переменную-член, вы можете установить возвращаемые данные как намерение, которое затем может быть обработано после 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;
    }
}
18
ответ дан Jed 21 August 2018 в 15:22
поделиться
  • 1
    В зависимости от того, какое действие вы делали, что не было разрешено, возможно, потребуется перейти к еще более позднему моменту, чем onResume (). Для insatcne, если проблема FragmentTransaction.commit (), это необходимо для входа в onPostResume (). – pjv 25 November 2012 в 19:22
  • 2
    Это ответ на этот вопрос для меня. Поскольку мне нужно было переслать полученный тег NFC в предыдущую активность, это то, что я сделал для меня. – Janis Peisenieks 16 July 2013 в 08:14
  • 3
    Для меня это происходило, потому что я не звонил super.onActivityResult(). – Sufian 10 February 2015 в 12:40

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

0
ответ дан Levon Petrosyan 21 August 2018 в 15:22
поделиться

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

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

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

popMyFragmentAndMoveOn();

на это:

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

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

1
ответ дан mojuba 21 August 2018 в 15:22
поделиться

Что касается @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 21 August 2018 в 15:22
поделиться

Проверьте, активна ли 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();
}
47
ответ дан Naskov 21 August 2018 в 15:22
поделиться

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

15
ответ дан Piyush 21 August 2018 в 15:22
поделиться
  • 1
    Ответ не скопирован или не реферирует форму else, где .Is был опубликован, чтобы помочь людям по моему рабочему решению, которое получено несколькими пробными версиями и ошибками – Vinayak 27 October 2017 в 14:25

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

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

Crashlytics по-прежнему отправляет мне это странное сообщение об ошибке.

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

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

Чтобы подвести итог, принятый ответ здесь может работать на версиях андроида до Нугата.

Это может спасти кого-то несколько часов поиска. счастливые кодировки. & lt; 3 ура

0
ответ дан ralphgabb 21 August 2018 в 15:22
поделиться

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

public void onBackPressed() {}

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

10
ответ дан saberrider 21 August 2018 в 15:22
поделиться

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

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

Я положил его в onStart, это было RIGHT

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

}
0
ответ дан Samet öztoprak 21 August 2018 в 15:22
поделиться

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

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

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

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

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

Вот что я чтобы решить эту проблему:

Поскольку невозможно избежать IllegalStateException в обратном вызове, catch & amp; игнорируйте его.

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;

И сохраните это состояние в bundle.

@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;
    }
}
96
ответ дан Synesso 21 August 2018 в 15:22
поделиться

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

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

0
ответ дан Volodymyr Khodonovych 21 August 2018 в 15:22
поделиться
15
ответ дан Piyush 1 November 2018 в 08:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: