IllegalStateException: nie można wykonać tej akcji po onSaveInstanceState z ViewPager

Otrzymuję raporty użytkowników z mojej aplikacji na rynku, dostarczając następujący wyjątek:

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)

Najwyraźniej ma to coś wspólnego z Fragmentmanagerem, którego nie używam. Stacktrace nie pokazuje żadnej z moich klas, więc nie mam pojęcia, gdzie występuje ten wyjątek i jak temu zapobiec.

Dla przypomnienia: mam tabhost, a w każdej karcie znajduje się grupa aktywności przełączająca się między aktywnościami.

Author: rds, 2011-09-28

26 answers

Proszę sprawdzić moją odpowiedź tutaj . W zasadzie musiałem:

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

Nie wykonuj połączenia z super() metodą saveInstanceState. To wszystko popsuło...

Jest to znany błąd w pakiecie wsparcia.

Jeśli chcesz zapisać instancję i dodać coś do swojego outState Bundle można użyć:

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

W końcu właściwe rozwiązanie było (jak widać w komentarzach) do użycia :

transaction.commitAllowingStateLoss();

Podczas dodawania lub wykonywania FragmentTransaction to było przyczyną Exception.

 636
Author: Ovidiu Latcu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:15

Istnieje wiele powiązanych problemów z podobnym Komunikatem o błędzie. Sprawdź drugą linię tego konkretnego śladu stosu. Wyjątek ten jest szczególnie związany z wywołaniem FragmentManagerImpl.popBackStackImmediate.

Wywołanie tej metody, podobnie jak popBackStack, spowoduje zawsze niepowodzenie z IllegalArgumentException, jeśli stan sesji został już zapisany. Sprawdź źródło. Nic nie możesz zrobić, aby powstrzymać ten wyjątek.

  • usunięcie połączenia do super.onSaveInstanceState nie pomoże.
  • Tworzenie fragmentu z Nie pomoże.

Oto jak zaobserwowałem problem:

  • jest formularz z przyciskiem submit.
  • Po kliknięciu przycisku tworzy się okno dialogowe i rozpoczyna się proces asynchroniczny.
  • użytkownik klika klucz domowy przed zakończeniem procesu - wywoływany jest onSaveInstanceState.
  • proces kończy się, wywołanie zwrotne jest i popBackStackImmediate jest próbowany.
  • IllegalStateException jest wyrzucony.

Oto, co zrobiłem, aby go rozwiązać:

Ponieważ nie jest możliwe, aby uniknąć IllegalStateException w wywołaniu zwrotnym, Złap i zignoruj go.

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There's no way to avoid getting this if saveInstanceState has already been called.
}

To wystarczy, aby powstrzymać awarię aplikacji. Ale teraz użytkownik przywróci aplikację i zobaczy, że Przycisk, o którym myślał, że nacisnął, w ogóle nie został naciśnięty (myślą). Fragment formularza wciąż się pokazuje!

Aby to naprawić, po utworzeniu okna dialogowego wprowadź stan wskazujący, że proces się rozpoczął.

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

I zapisz ten stan w pakiecie.

@Override
public void onSaveInstanceState(Bundle outState) {
    ...
    outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

Nie zapomnij załaduj go ponownie w onViewCreated

Następnie, po wznowieniu, wycofaj fragmenty, jeśli wcześniej próbowano przesłać. Uniemożliwia to użytkownikowi powrót do tego, co wydaje się nie przesłanym formularzem.

@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;
    }
}
 99
Author: Synesso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-15 21:57:25

Sprawdź czy aktywność {[1] } przed pokazaniem fragmentu i zwróć uwagę na commitAllowingStateLoss().

Przykład:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}
 47
Author: Naskov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-26 08:10:27

Oto inne rozwiązanie tego problemu.

Używając prywatnej zmiennej członkowskiej, możesz ustawić zwrócone dane jako intencję, która może być następnie przetworzona po super.onResume ();

Like so:

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;
    }
}
 19
Author: Jed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-16 05:44:06

Jest Październik 2017, a Google tworzy bibliotekę wsparcia dla Androida z nowym komponentem things call Lifecycle. Dostarcza nowego pomysłu na ten problem "nie można wykonać tej akcji po onSaveInstanceState".

W skrócie:

  • użyj komponentu cyklu życia, aby określić, czy to właściwy czas na wyświetlenie fragmentu.

Dłuższa wersja z wyjaśnieniem:

  • Dlaczego ten problem wyszedł na jaw?

    To dlatego, że próbujesz użyć FragmentManager ze swojej aktywności (która będzie trzymać Twój fragment, jak przypuszczam?), aby zatwierdzić transakcję dla Ciebie. Zazwyczaj wygląda to tak, jakbyś próbował wykonać jakąś transakcję dla nadchodzącego fragmentu, tymczasem aktywność hosta już wywołuje metodę savedInstanceState (Użytkownik może się zdarzyć, że dotknie przycisku home, więc aktywność wywołuje onStop(), w moim przypadku jest to powód) {]}

    Zazwyczaj ten problem nie powinien się zdarzyć - zawsze staramy się załadować fragment do aktywności na samym Początek, jak metoda onCreate() jest do tego idealnym miejscem. Ale czasami zdarza się to , zwłaszcza gdy nie możesz zdecydować, który fragment załadujesz do tej czynności, lub próbujesz załadować fragment z bloku AsyncTask (lub cokolwiek zajmie trochę czasu). Czas, zanim transakcja fragmentu naprawdę się wydarzy, ale po metodzie onCreate() aktywności, użytkownik może zrobić wszystko. Jeśli użytkownik naciśnie przycisk home, który uruchomi metodę onSavedInstanceState(), pojawi się can not perform this action crash.

    Jeśli ktoś chce zajrzeć głębiej w ten temat, proponuję zajrzeć na ten blog post . Wygląda głęboko w warstwie kodu źródłowego i wyjaśnia wiele na ten temat. Ponadto, podaje powód, dla którego nie powinieneś używać metody commitAllowingStateLoss(), aby obejść tę awarię (zaufaj mi, nie oferuje ona nic dobrego dla Twojego kodu)

  • Jak to naprawić?

    • Czy powinienem użyć metody commitAllowingStateLoss() do załadowania fragmentu? Nope you nie powinno;

    • Czy powinienem nadpisać metodę onSaveInstanceState, zignorować metodę super wewnątrz niej? nie powinieneś;

    • Czy powinienem użyć magicznej aktywności isFinishing wewnątrz, aby sprawdzić, czy aktywność hosta jest we właściwym momencie dla transakcji fragmentarycznej? Tak, to wygląda na właściwą drogę.

  • Spójrz na to, co może zrobić komponent cyklu życia .

    W zasadzie Google sprawia, że niektóre implementacja wewnątrz klasy AppCompatActivity (i kilku innych klas bazowych, których powinieneś użyć w projekcie), co ułatwia określenie bieżącego stanu cyklu życia. Przyjrzyjmy się naszemu problemowi: dlaczego miałby się on wydarzyć? To dlatego, że robimy coś w złym momencie. Więc staramy się tego nie robić, a ten problem zniknie.

    Trochę koduję do własnego projektu, oto co robię używając LifeCycle. Koduję w Kotlinie.

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()
    }

Jak pokazałem powyżej. I sprawdzi stan cyklu życia aktywności hosta. W przypadku komponentu cyklu życia w bibliotece Wsparcia może to być bardziej szczegółowe. Kod lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED) oznacza, jeśli obecny stan jest co najmniej onResume, nie później niż to? Co sprawia, że moja metoda nie będzie wykonywana podczas innego stanu życia(jak onStop).

  • Wszystko gotowe? Oczywiście, że nie. Kod, który pokazałem, mówi o nowym sposobie zapobiegania awarii aplikacji. Ale jeśli dojdzie do stanu onStop, ta linia kod nie robi rzeczy, a tym samym nic nie pokazuje na ekranie. Kiedy użytkownicy wrócą do aplikacji, zobaczą pusty ekran, czyli pustą aktywność hosta, nie pokazującą żadnych fragmentów. To złe doświadczenie(tak trochę lepsze niż awaria).

    Więc chciałbym, żeby było coś ładniejszego: aplikacja nie zawiesi się, jeśli dojdzie do stanu życia później niż onResume, metoda transakcji jest świadomy stanu życia; poza tym, aktywność będzie próbować kontynuować, aby zakończyć tę transakcję fragment akcja, po powrocie użytkownika do naszej aplikacji.

    Dodaję coś więcej do tej metody:

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()
        }
    }
}

Utrzymuję listę wewnątrz tej klasy dispatcher, aby przechowywać te fragmenty nie mają szans na zakończenie akcji transakcyjnej. A gdy użytkownik wróci z ekranu głównego i stwierdzi, że fragment nadal czeka na uruchomienie, przejdzie do metody resume() pod adnotacją @OnLifecycleEvent(Lifecycle.Event.ON_RESUME). Teraz myślę, że powinno działać tak, jak się spodziewałem.

 16
Author: Anthonyeef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-04 15:21:45

Rozwiązanie krótkie i robocze:

Wykonaj Proste Kroki]}

Kroki

Krok 1: nadpisanie onSaveInstanceState stanu w odpowiednim fragmencie. I usuń z niego super metodę.

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

Krok 2: Użycie fragmentTransaction.commitAllowingStateLoss( );

Zamiast fragmentTransaction.commit( ); podczas operacji fragment.

 15
Author: Vinayak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 13:20:44

Uwaga , użycie transaction.commitAllowingStateLoss() może spowodować złe wrażenia dla użytkownika. Aby uzyskać więcej informacji na temat tego wyjątku, Zobacz ten post .

 11
Author: Eric Brandwein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-13 15:29:08

Znalazłem brudne rozwiązanie tego rodzaju problemu. Jeśli nadal chcesz zachować ActivityGroups z jakiegokolwiek powodu( miałem powody ograniczenia czasu), po prostu zaimplementuj

public void onBackPressed() {}

W twoim Activity i zrób tam jakiś back kod. nawet jeśli nie ma takiej metody na starszych urządzeniach, metoda ta zostanie wywołana przez nowsze.

 10
Author: saberrider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-21 13:10:59

Nie używaj metody commithallowingstateloss (), powinna być używana tylko w przypadkach, gdy jest w porządku, aby stan interfejsu użytkownika nieoczekiwanie zmienił się u użytkownika.

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

Jeśli transakcja ma miejsce w ChildFragmentManager programu parentFragment, użyj parentFragment.isResume () na zewnątrz, aby sprawdzić zamiast tego.

if (parentFragment.isResume()) {
    DummyFragment dummyFragment = DummyFragment.newInstance();
    transaction = childFragmentManager.BeginTransaction();
    trans.Replace(Resource.Id.fragmentContainer, startFragment);
}
 6
Author: Chandler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 05:19:57

Miałem podobny problem, scenariusz był taki:

  • Moja aktywność polega na dodawaniu / zastępowaniu fragmentów listy.
  • każdy fragment listy ma odniesienie do aktywności, aby powiadomić aktywność, gdy element listy zostanie kliknięty (wzór obserwatora).
  • każdy fragment listy wywołuje setRetainInstance(true);w swojej metodzie onCreate.

Metoda onCreate działania wyglądała następująco:

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

Wyjątkiem był wrzucony, ponieważ zmienia się konfiguracja kiedy (urządzenie się obraca), zostaje utworzona aktywność, główny fragment jest pobierany z historii menedżera fragmentów i jednocześnie fragment ma już stare odniesienie do zniszczonej aktywności

Zmiana implementacji na to rozwiązała problem:

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

Musisz ustawić słuchaczy za każdym razem, gdy aktywność jest tworzona, aby uniknąć sytuacji, w której fragmenty mają odniesienia do starych zniszczone przypadki działalności.

 5
Author: Mina Samy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-18 12:28:37

Otrzymywałem ten wyjątek, gdy naciskałem przycisk Wstecz, aby anulować wybór intencji na mojej aktywności fragmentu mapy. Rozwiązałem to, zamieniając kod onResume (gdzie inicjalizowałem fragment) na OnStart () i aplikacja działa dobrze.Mam nadzieję, że to pomoże.

 3
Author: DCS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-14 11:35:34

Myślę, że użycie transaction.commitAllowingStateLoss(); nie jest najlepszym rozwiązaniem. Ten wyjątek zostanie wyrzucony, gdy zmieni się konfiguracja aktywności i zostanie wywołany fragment onSavedInstanceState(), a następnie twoja metoda wywołania zwrotnego asynchronicznego spróbuje zatwierdzić fragment.

Prostym rozwiązaniem może być sprawdzenie czy aktywność zmienia konfigurację czy nie

Np. sprawdź isChangingConfigurations()

Tzn.

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

SprawdźTen link również

 2
Author: Amol Desai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-13 07:40:21

Jeśli robisz FragmentTransaction w onActivityResult co możesz zrobić możesz ustawić jakąś wartość logiczną wewnątrz onActivityResult następnie w onResume możesz zrobić FragmentTransaction na podstawie wartości logicznej. Proszę zapoznać się z poniższym kodem.

@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
Author: anoopbryan2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-07 06:46:25

Prawdopodobnie najłagodniejszym i najprostszym rozwiązaniem, jakie znalazłem w moim przypadku, było uniknięcie wyrzucania obrażającego fragmentu ze stosu w odpowiedzi na wynik działania. Więc zmiana tego połączenia w moim onActivityResult():

popMyFragmentAndMoveOn();

Do tego:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    public void run() {
        popMyFragmentAndMoveOn();
    }
}
Pomogłem w moim przypadku.
 1
Author: mojuba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-02 16:52:01

Za każdym razem, gdy próbujesz załadować fragment do swojej aktywności, upewnij się, że aktywność jest w wznowieniu i nie będzie pauzować state.In stan pauzy może skończyć się utratą operacji commit, która została wykonana.

Możesz użyć transakcji.commitAllowingStateLoss () zamiast transaction.commit () to load fragment

Lub

Utwórz wartość logiczną i sprawdź, czy aktywność nie będzie działać w onpause

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

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

Następnie podczas wczytywania sprawdź fragment

if(mIsResumed){
//load the your fragment
}
 1
Author: Anonymous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-13 07:07:06

W odniesieniu do @Anthonyeef świetna odpowiedź, oto przykładowy kod w Javie:

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
Author: MorZa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-17 12:10:03

Dodaj to do swojej aktywności

@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
Author: yifan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-03 12:35:06

Począwszy od biblioteki wsparcia w wersji 24.0.0 można wywołać metodę FragmentTransaction.commitNow(), która zatwierdzi tę transakcję synchronicznie zamiast wywoływać commit(), a następnie executePendingTransactions(). Jak Dokumentacja mówi to podejście jeszcze lepiej:

Wywołanie commitNow jest lepsze niż wywołanie commit (), po którym następuje executePendingTransactions (), ponieważ ta ostatnia będzie miała efekt uboczny próby zatwierdzenia wszystkich aktualnie oczekujących transakcji, niezależnie od tego, czy jest to pożądane zachowanie, czy nie.

 0
Author: Volodymyr Khodonovych,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-29 20:22:23

Również doświadczyłem tego problemu i problem pojawia się za każdym razem, gdy zmienia się kontekst twojego FragmentActivity (np. zmienia się orientacja ekranu itp.). Więc najlepszą poprawką jest zaktualizowanie kontekstu z FragmentActivity.

 0
Author: Adam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 13:21:05

Wyjątek rzuca się tutaj (w fragmencie):

@Override
public void onBackPressed() {
    if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
        super.onBackPressed();
    }
}

W FragmentManager.popBackStatckImmediate()FragmentManager.checkStateLoss() nazywa się po pierwsze. To jest przyczyna IllegalStateException. Zobacz implementację poniżej:

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

Rozwiązuję ten problem po prostu za pomocą flagi, aby zaznaczyć aktualny status aktywności. Oto moje rozwiązanie:

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
Author: Frost Lau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-07 06:19:20

Skończyłem z utworzeniem fragmentu bazowego i sprawiłem, że wszystkie fragmenty w mojej aplikacji rozszerzają go

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

Wtedy gdy próbuję pokazać fragment używam showAllowingStateLoss zamiast show

Tak:

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

Doszedłem do tego rozwiązania z tego PR: https://github.com/googlesamples/easypermissions/pull/170/files

 0
Author: Ahmad El-Melegy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-15 20:11:25

Inne możliwe obejście, które nie jestem pewien, czy pomaga we wszystkich przypadkach (pochodzenie Proszę.) :

@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
Author: android developer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-09 08:26:14

Wiem, że jest akceptowana odpowiedź przez @Ovidiu Latcu, ale po jakimś czasie błąd nadal utrzymuje się.

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

Crashlytics wciąż wysyła mi ten dziwny komunikat o błędzie.

Jednak błąd występujący teraz tylko w wersji 7+ (Nugat) Moją poprawką było użycie commit () zamiast commit() w fragmentTransaction.

Ten post {[11] } jest pomocny dla commit callowingstateloss () i nigdy więcej nie miał problemu z fragmentem.

Podsumowując, zaakceptowana odpowiedź tutaj może działać na wersjach Pre Nougat android.

To może zaoszczędzić komuś kilka godzin poszukiwań. wesołego kodowania.
 0
Author: ralphgabb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-18 05:06:06

Miałem dokładnie ten sam problem. Stało się to z powodu zniszczenia poprzedniej działalności. po zakończeniu poprzedniej działalności został zniszczony. I put it base activity (WRONG)

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

Włożyłem go do onstartu to było słuszne

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

}
 0
Author: Samet öztoprak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 06:13:16

Aby ominąć ten problem, możemy użyć komponentu architektury nawigacji , który został wprowadzony w Google I/O 2018. Komponent Architektura nawigacji upraszcza implementację nawigacji w aplikacji na Androida.

 0
Author: Levon Petrosyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-22 12:09:38

Dzięki Uprzejmości: rozwiązanie dla IllegalStateException

Ten problem denerwował mnie przez długi czas, ale na szczęście przyszedłem z konkretnym rozwiązaniem. Szczegółowe wyjaśnienie tego jest tutaj .

Użycie commitAllowStateloss() może zapobiec temu wyjątkowi, ale prowadzi do interfejsu użytkownika irregularities.So far zrozumieliśmy, że IllegalStateException występuje, gdy próbujemy zatwierdzić fragment po utracie stanu aktywności - więc powinniśmy po prostu opóźnić transakcja aż do stanu restored.It można po prostu zrobić tak

Zadeklaruj dwie prywatne zmienne logiczne

 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;

Teraz w onpostresume() i onPause ustawiamy i wyłączamy naszą boolowską zmienną isTransactionSafe. Chodzi o to, aby oznaczyć trasnsaction bezpiecznie tylko wtedy, gdy aktywność jest na pierwszym planie, więc nie ma szans na 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();
    }
}

- to, co zrobiliśmy do tej pory, uratuje przed nielegalnym przyjęciem, ale nasze transakcje zostaną utracone, jeśli zostaną wykonane po przeniesieniu aktywności do tła, coś w rodzaju commit callowstateloss (). Aby w tym pomóc, mamy zmienną boolean 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
Author: IrshadKumail,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-30 17:35:19