ottenere l'eccezione "IllegalStateException: impossibile eseguire questa azione dopo onSaveInstanceState"


355

Ho un'applicazione Android Live e dal mercato ho ricevuto la seguente traccia dello stack e non ho idea del perché accada perché non sta accadendo nel codice dell'applicazione, ma è causato da alcuni o dall'altro evento dall'applicazione (presupposto)

Non sto usando Fragments, c'è ancora un riferimento a FragmentManager. Se un corpo può far luce su alcuni fatti nascosti per evitare questo tipo di problema:

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)  

Hai già trovato una soluzione? Avendo lo stesso problema qui: stackoverflow.com/questions/7575921/...
nhaarman

1
Ho avuto lo stesso problema e
phlebas il

2
@phlebas No, non l'hai fatto. I tuoi riguardano i dialoghi, e questo no. La riga superiore della corrispondenza della traccia dello stack non è sufficiente. Il resto è molto diverso. Lo dico perché ho appena esaminato il tuo problema e sfortunatamente non mi è stato di aiuto.
themightyjon

Usi un thread o un AsynTask in quell'attività?
José Castro,

21
Discuto questo errore nel mio post sul blog ... dovresti leggerlo. :)
Alex Lockwood,

Risposte:


455

Questo è l'insetto più stupido che ho riscontrato finora. Ho avuto Fragmentun'applicazione perfettamente funzionante per API <11 e Force Closingsu API> 11 .

Non riuscivo davvero a capire cosa fossero cambiati nel Activityciclo di vita della chiamata a saveInstance, ma ecco come ho risolto questo:

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

Semplicemente non faccio la chiamata a .super()e tutto funziona alla grande. Spero che questo ti farà risparmiare un po 'di tempo.

EDIT: dopo qualche altra ricerca, questo è un bug noto nel pacchetto di supporto.

Se è necessario salvare l'istanza e aggiungere qualcosa al proprio outState Bundleè possibile utilizzare quanto segue:

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

EDIT2: questo può verificarsi anche se stai tentando di eseguire una transazione dopo che Activityè passato in background. Per evitarlo dovresti usarecommitAllowingStateLoss()

EDIT3: Le soluzioni di cui sopra stavano risolvendo problemi nelle prime librerie support.v4 da quello che ricordo. Ma se hai ancora problemi con questo DEVI anche leggere il blog di @AlexLockwood : Fragment Transactions & Activity Loss

Riepilogo dal post del blog (ma ti consiglio vivamente di leggerlo):

  • MAI commit() transazioni dopo onPause()pre-Honeycomb e onStop()post-Honeycomb
  • Prestare attenzione quando si eseguono transazioni all'interno dei Activitymetodi del ciclo di vita. Uso onCreate() , onResumeFragments()eonPostResume()
  • Evitare di eseguire transazioni all'interno di metodi di callback asincroni
  • Utilizzare commitAllowingStateLoss()solo come ultima risorsa

97
Dovresti usare commitAllowingStateLoss () invece di commit ()
meh

7
Quindi, non chiamare super in onSaveInstanceState impedirà a FragmentManager di salvare lo stato di tutti i frammenti e ripristinarli. Ciò potrebbe causare problemi con la rotazione. Inoltre, ho appena provato l'altra cosa a mettere spazzatura nel pacchetto e questo non fa differenza per me. Non sono sicuro di come - il bug a cui hai fatto riferimento nel pacchetto di supporto è una NullPointerException e non sembra molto simile a questa IllegalStateException ...
themightyjon

56
@meh commitAllowingStateLoss()evita solo l'eccezione. Non protegge l'applicazione dalla perdita accidentale dello stato. Vedi questo post sul blog .
Alex Lockwood,

2
@AlexLockwood così da quel post sul blog possiamo imparare che dovremmo fare tutte le nostre chiamate di rete all'interno del frammento (e mostrare alcuni progressi temporanei se necessario) e questo è l'unico modo in cui possiamo evitare di ottenere questa eccezione quando probabilmente si verifica perché commit viene chiamato dopo una chiamata di metodo asincrona.
Meh

1
Non capisco il primo punto: "MAI impegnare transazioni () dopo ... onStop () su post-Honeycomb". Cosa succede se ho bisogno di un pulsante per attivare la sostituzione di un frammento con un altro? Devo inserire un valore booleano che controlla se l'attività è terminata su Stop e, in caso affermativo, chiamare commitAllowingStateLoss? Inoltre, cosa succede se ho un frammento all'interno di un frammento, che devo sostituire facendo clic su un pulsante?
sviluppatore Android

76

Guardando nel codice sorgente Android sulle cause che causano questo problema, viene visualizzato il flag mStateSaved FragmentManagerImpl classe (istanza disponibile in Activity) ha valore true. È impostato su true quando lo stack posteriore viene salvato (saveAllState) su chiamata da Activity#onSaveInstanceState. Successivamente le chiamate da ActivityThread non ripristinano questo flag usando i metodi di ripristino disponibili da FragmentManagerImpl#noteStateNotSaved()e dispatch().

Per come la vedo io ci sono alcune correzioni disponibili, a seconda di ciò che la tua app sta facendo e usando:

Buoni modi

Prima di ogni altra cosa: vorrei pubblicizzare l' articolo di Alex Lockwood . Quindi, da quello che ho fatto finora:

  1. Per frammenti e attività che non necessitano di conservare le informazioni sullo stato, chiama commitAllowStateLoss . Tratto dalla documentazione:

    Consente l'esecuzione del commit dopo il salvataggio dello stato di un'attività. Questo è pericoloso perché il commit può essere perso se l'attività deve essere successivamente ripristinata dal suo stato, quindi dovrebbe essere utilizzata solo nei casi in cui è corretto che lo stato dell'interfaccia utente cambi in modo imprevisto sull'utente`. Immagino che questo va bene se il frammento mostra informazioni di sola lettura. O anche se mostrano informazioni modificabili, utilizzare i metodi di callback per conservare le informazioni modificate.

  2. Subito dopo il commit della transazione (che hai appena chiamato commit()), effettua una chiamata aFragmentManager.executePendingTransactions() .

Modi non consigliati:

  1. Come Ovidiu Latcu ha menzionato sopra, non chiamare super.onSaveInstanceState() . Ma questo significa che perderai l'intero stato della tua attività insieme allo stato dei frammenti.

  2. Sostituisci onBackPressede solo in chiamata finish(). Questo dovrebbe essere OK se l'applicazione non utilizza l'API Fragments; come insuper.onBackPressed c'è una chiamata a FragmentManager#popBackStackImmediate().

  3. Se stai utilizzando entrambe le API di frammenti e lo stato della tua attività è importante / vitale, puoi provare a chiamare utilizzando l'API di riflessione FragmentManagerImpl#noteStateNotSaved(). Ma questo è un trucco, o si potrebbe dire che è una soluzione alternativa. Non mi piace, ma nel mio caso è abbastanza accettabile poiché ho un codice da un'app legacy che utilizza codice deprecato ( TabActivitye implicitamente LocalActivityManager).

Di seguito è riportato il codice che utilizza la riflessione:

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

Saluti!


Questo sembra accadere anche con ActionBarSherlock, sotto Gingerbread, quindi il caso di controllare Id build sembra discutibile ... :(
t0mm13b,

Inoltre, dovresti sottolineare - che non è adatto nemmeno all'uso dell'ABS :)
t0mm13b,

@ t0mm13b: il codice sopra sta per il mio progetto in quanto non utilizza né frammenti, né support.FragmentActivity. Funziona su android.app.Activity e poiché l'incongruenza che innesca l'eccezione è causata da FragmentManager (livello API 11 verso l'alto), ecco perché il controllo ... Se ritieni che la fonte del male sia la stessa anche per te, sentiti libero per rimuovere il segno di spunta. ABS è una storia diversa in quanto funziona in cima al pacchetto di compatibilità e all'implementazione del supporto.FragmentActivity può usare la stessa implementazione di FragmentManager e voilà: stesso problema.
Gunar

@ t0mm13b: per aggiungere altro in quanto 600 caratteri non sono sufficienti, devi prima esaminare cosa ti sta causando davvero. Devi anche capire che sopra è un brutto hack e non mi assumo alcuna responsabilità se funziona o no (per me è stata la soluzione migliore considerando le circostanze). Se devi usarlo, ricontrolla il codice sorgente compat per i nomi delle variabili poiché potrebbero differire dal pacchetto standard. Spero che questo problema venga affrontato dalle prossime versioni del pacchetto di compatibilità, ma dall'esperienza Android ci sono poche possibilità che accada ...
gunar,

Uhhh ... È esattamente lo stesso problema di questo bug report che è il punto della domanda di questo PO. Mi alzo dal mio commento - si dovrebbe hanno esplicitamente messo in una dichiarazione di non responsabilità e dire che non è garantito e anche dovuto hanno dichiarato che non è stato utilizzato frammenti - altrimenti perché preoccuparsi distacco che la risposta sia! :)
Sto

35

Tale eccezione si verificherà se si tenta di eseguire una transizione di frammento dopo che onSaveInstanceState()viene chiamata l' attività del frammento .

Uno dei motivi per cui ciò può accadere è se si lascia uno AsyncTask(o Thread) in esecuzione quando un'attività viene interrotta.

Eventuali transizioni dopo onSaveInstanceState() richiamo potrebbero potenzialmente andare perse se il sistema recupera l'attività per le risorse e la ricrea in un secondo momento.


2
Ehi Funk, ho una domanda qui, come mai onBackPressed può essere chiamato su quell'attività o quel frammento, se quell'attività o quel frammento è stato fermato. L'eccezione di cui sopra sembra essere generata da un evento dell'interfaccia utente (ovvero premendo il tasto BACK), tuttavia non sono in grado di scoprire la relazione tra l'attività asincrona e il tasto Indietro.
Dcool

Dal momento che è possibile salvare le transizioni dei frammenti nello stato indietro, premendo il pulsante Indietro si può causare il contrario della transizione salvata (così i vecchi frammenti ritornano). onSaveInstanceState viene chiamato prima che l'attività venga distrutta per ripristinare le risorse nel sistema, non sempre dopo la chiamata di onStop. Spiacente, non era molto chiaro nella mia risposta.
FunkTheMonk

Funk, ma non utilizzo frammenti nella mia applicazione. Potrebbero essere i frammenti utilizzati nel codice nativo. prima pensavo stessi parlando della stessa cosa.
Dcool

1
Ho avuto un AsyncTask con riferimento a un frammento. Problema risolto dopo aver rimosso la chiamata super () da onSaveInstanceState e sostituito il riferimento dal mio AsyncTask con un WeakReference <Fragment>.
Buffalo,

2
@Buffalo Questa non è sicuramente una soluzione al problema. Dovresti sempre chiamare super.onSaveInstanceState().
Alex Lockwood,

27

Basta chiamare super.onPostResume () prima di mostrare il frammento o spostare il codice nel metodo onPostResume () dopo aver chiamato super.onPostResume (). Questo risolve il problema!


6
Chiamare onPostResume () assicura che onResumeFragments () venga chiamato e per me questa è la soluzione ideale.
j2emanue,

20

Questo può accadere anche quando si richiama dismiss()un frammento della finestra di dialogo dopo che lo schermo è stato bloccato \ oscurato e lo stato dell'istanza della finestra di dialogo Attività + è stato salvato. Per aggirare questa chiamata:

dismissAllowingStateLoss()

Letteralmente ogni volta che sto chiudendo una finestra di dialogo, non mi interessa più il suo stato, quindi va bene lo stesso - in realtà non stai perdendo alcuno stato.


2
Questo è stato il mio problema esatto! Signore, siete geniali!
Tash Pemhiwa,

17

Soluzione breve e funzionante:

Segui semplici passaggi:

Passaggio 1 : sovrascrivere lo stato onSaveInstanceState nel rispettivo frammento. E rimuovi il metodo super da esso.

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

Passaggio 2 : utilizzare CommitAllowingStateLoss (); invece di commit (); mentre operazioni sui frammenti.

fragmentTransaction.commitAllowingStateLoss();

1
Rimuovere il super metodo ha funzionato, puoi spiegare perché? È sicuro rimuoverlo?
Bruce,

7
non è sicuro rimuovere super (), dopo perderai altri dati conf!
pesce morto


7

questo ha funzionato per me ... l'ho scoperto da solo ... spero che ti aiuti!

1) NON avere un FragmentManager / FragmentTransaction globale "statico".

2) onCreate, inizializza SEMPRE di nuovo FragmentManager!

campione sotto: -

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

6

Lo stavo sempre ricevendo quando provavo a mostrare il frammento nel metodo onActivityForResult (), quindi il problema era successivo:

  1. La mia attività viene messa in pausa e interrotta, il che significa che onSaveInstanceState () era già stato chiamato (per entrambi i dispositivi pre-Honeycomb e post-Honeycomb).
  2. In caso di qualsiasi risultato, ho effettuato una transazione per mostrare / nascondere il frammento, causando questo IllegalStateException.

Quello che ho fatto è il prossimo:

  1. Valore aggiunto per determinare se l'azione desiderata è stata eseguita (ad es. Scattare foto da camere - isPhotoTaken) - può essere un valore booleano o intero a seconda di quante diverse transazioni sono necessarie.
  2. Nel metodo overRiden onResumeFragments () ho verificato il mio valore e dopo aver effettuato le transazioni di frammento di cui avevo bisogno. In questo caso, commit () non è stato eseguito dopo onSaveInstanceState, poiché lo stato è stato restituito nel metodo onResumeFragments ().

5

Ho risolto il problema con onconfigurationchanged. Il trucco è che in base al ciclo di vita dell'attività Android, quando hai esplicitamente chiamato un intento (intento della fotocamera o qualsiasi altro); l'attività viene messa in pausa e onsavedInstance viene chiamato in quel caso. Quando si ruota il dispositivo in una posizione diversa da quella durante la quale l'attività era attiva; l'esecuzione di operazioni sui frammenti come il commit dei frammenti provoca un'eccezione di stato illegale. Ci sono molte lamentele al riguardo. Riguarda la gestione del ciclo di vita delle attività Android e le chiamate ai metodi corretti. Per risolverlo ho fatto questo: 1-Ignora il metodo onsavedInstance della tua attività e determina l'orientamento dello schermo corrente (verticale o orizzontale), quindi imposta l'orientamento dello schermo su di esso prima che l'attività venga messa in pausa. in questo modo l'attività blocca la rotazione dello schermo per la tua attività nel caso in cui sia stata ruotata da un'altra. 2, quindi, ignora il metodo di attività onresume e imposta ora la tua modalità di orientamento sul sensore in modo che dopo che sia stato chiamato il metodo salvato, richiamerà ancora una volta la configurazione per gestire correttamente la rotazione.

Puoi copiare / incollare questo codice nella tua attività per gestirlo:

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

4

Ho avuto lo stesso problema, ottenere IllegalStateException, ma la sostituzione di tutte le mie chiamate a commit () con commitAllowingStateLoss () non ha aiutato.

Il colpevole era una chiamata a DialogFragment.show ().

Lo circondo

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

e quello lo ha fatto. OK, non riesco a mostrare la finestra di dialogo, ma in questo caso andava bene.

Era l'unico posto nella mia app in cui ho chiamato FragmentManager.beginTransaction () ma non ho mai chiamato commit (), quindi non l'ho trovato quando ho cercato "commit ()".

La cosa divertente è che l'utente non lascia mai l'app. Invece il killer era un annuncio interstiziale AdMob che veniva visualizzato.


3
Anch'io. Ho risolto Sovrascrivere il metodo 'show (gestore FragmentManager, String string)', sostituendo 'commit' con 'commitAllowingStateLoss'; perdere qualcosa perché non riesco a impostare due attributi privati ​​della finestra di dialogo: mDismissed e mShownByMe. Ma sembra funzionare ogni volta :)
Francesco Ditrani,

Ho creato una soluzione alternativa a DialogFragment, che può evitare questa eccezione: github.com/AndroidDeveloperLB/DialogShard
sviluppatore Android

4

La mia soluzione per quel problema era

Nel frammento aggiungi metodi:

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

Può essere brutto, ma non ho trovato niente di meglio.


Veri ... catturare l'eccezione può evitare l'arresto anomalo dell'applicazione, ma i problemi di comportamento che tali frammenti vengono lasciati sullo schermo o non vengono aggiunti.
Marcos Vasconcelos,

4
continua a scorrere. la verità è là fuori
anil

4

Ho riscontrato questo problema, ma penso che questo problema non sia correlato a commit e commitAllowStateLoss.

La seguente traccia dello stack e il messaggio di eccezione riguarda 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)

Ma questa eccezione è stata causata da 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)

Sono stati tutti causati da 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 sarà vero dopo onSaveInstanceState.

Questo problema si verifica raramente. Non ho mai riscontrato questo problema. Non riesco a ripresentarlo.

Ho trovato il problema 25517

Potrebbe essersi verificato nelle seguenti circostanze

  1. Il tasto Indietro viene chiamato dopo onSaveInstanceState, ma prima dell'avvio della nuova attività.

  2. usa onStop () nel codice

Non sono sicuro di quale sia la radice del problema. Quindi ho usato una brutta strada.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}

Non ho davvero risolto il problema, ma questo problema non è correlato a commit e commitAllowStateLoss.
oO_ox,

4

Ho lo stesso problema nella mia app. Sono stato risolto questo problema chiamando semplicemente la super.onBackPressed();classe precedente e chiamando la commitAllowingStateLoss()classe corrente con quel frammento.


2
Grazie. Questa soluzione risolto problema uisng commitAllowingStateLoss()invece dicommit()
Chintak Patel

Evita di utilizzare commitAllowingStateLoss () medium.com/@elye.project/…
swooby

3

onSaveInstance verrà chiamato se un utente ruota lo schermo in modo che possa caricare le risorse associate al nuovo orientamento.

È possibile che questo utente abbia ruotato lo schermo seguito premendo il pulsante Indietro (perché è anche possibile che questo utente abbia sfogliato il telefono mentre utilizzava l'app)


2
Sebbene le modifiche alla configurazione (come le modifiche all'orientamento) possano comportare questa eccezione, non sono la causa principale.
Alex Lockwood,


2

Lo stesso problema da me e dopo una lunga giornata di analisi di tutti gli articoli, blog e stackoverflow ho trovato una soluzione semplice. Non usare affatto salvatoInstanceState, questa è la condizione con una riga di codice. Sul codice del frammento:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....

2

Ciò accade ogni volta che si tenta di caricare un frammento ma l'attività ha cambiato il suo stato in onPause (). Ciò accade ad esempio quando si tenta di recuperare i dati e caricarli nell'attività, ma quando l'utente ha fatto clic su un pulsante e ha spostato alla prossima attività.

Puoi risolverlo in due modi

È possibile utilizzare transazione.commitAllowingStateLoss () invece diaction.commit () per caricare il frammento ma si potrebbe finire per perdere l'operazione di commit eseguita.

o

Accertarsi che l'attività sia in ripresa e che non venga messa in pausa durante il caricamento di un frammento. Crea un valore booleano e controlla se l'attività non passa allo stato onPause ().

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

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

quindi durante il caricamento del frammento controlla se è presente attività e carica solo quando l'attività è in primo piano.

if(mIsResumed){
 //load the fragment
}

1

Grazie @gunar, ma penso che ci sia un modo migliore.

Secondo il documento:

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

Quindi utilizzare commitNowper sostituire:

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()

0

Bene, dopo aver provato tutte le soluzioni di cui sopra senza successo (perché fondamentalmente non ho transazioni).

Nel mio caso stavo usando AlertDialogs e ProgressDialog come frammenti che, a volte, durante la rotazione, quando richiedevo il FragmentManager, l'errore aumenta.

Ho trovato una soluzione alternativa mescolando alcuni post simili:

È una soluzione in 3 passaggi, tutto fatto su FragmentActivity (in questo caso, si chiama 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

Quando utilizzo l'inattività in un frammento, ottengo questa eccezione;

Quando cambio a utilizzare startactivityforresult, l'eccezione scompare :)

Quindi il modo più semplice per risolverlo è usare startActivityForResult api :)


0

Stavo ottenendo questa eccezione quando stavo premendo il pulsante Indietro per annullare la selezione dell'intento sull'attività del frammento della mappa. Ho risolto questo problema sostituendo il codice di onResume () (in cui stavo inizializzando il frammento e stavo eseguendo la transazione) in onStart () e l'app ora funziona correttamente. Spero che sia d'aiuto.


0

Questo problema è stato risolto in Android 4.2 e anche nella fonte della libreria di supporto. [*]

Per dettagli sulla causa (e soluzioni alternative) fare riferimento alla segnalazione di bug di Google: http://code.google.com/p/android/issues/detail?id=19917

Se stai utilizzando la libreria di supporto, non dovresti preoccuparti di questo bug (a lungo) [*]. Tuttavia, se si utilizza direttamente l'API (ovvero non si utilizza FragmentManager della libreria di supporto) e si sceglie come target un'API sotto Android 4.2, è necessario provare una soluzione.

[*] Al momento in cui scrivo Android SDK Manager sta ancora distribuendo una versione precedente che presenta questo errore.

Modifica Ho intenzione di aggiungere alcuni chiarimenti qui perché ovviamente ho in qualche modo confuso chiunque abbia votato in negativo questa risposta.

Esistono diverse (ma correlate) circostanze che possono causare il lancio di questa eccezione . La mia risposta sopra si riferisce all'istanza specifica discussa nella domanda, ad esempio un bug in Android che è stato successivamente corretto. Se ricevi questa eccezione per un altro motivo è perché stai aggiungendo / rimuovendo frammenti quando non dovresti esserlo (dopo che gli stati dei frammenti sono stati salvati). Se ti trovi in ​​una situazione del genere, forse " Nested Fragments - IllegalStateException" Impossibile eseguire questa azione dopo onSaveInstanceState " " può esserti utile.



0

Il mio caso d'uso: ho usato l'ascoltatore in frammenti per notificare all'attività che qualcosa è successo. Ho eseguito il commit di nuovi frammenti sul metodo di callback. Funziona perfettamente alla prima volta. Ma al cambio di orientamento l'attività viene ricreata con lo stato dell'istanza salvato. In tal caso il frammento non creato di nuovo implica che il frammento abbia l'ascoltatore che è vecchia attività distrutta. Ad ogni modo il metodo di richiamata verrà attivato all'azione. Va alle attività distrutte che causano il problema. La soluzione è ripristinare l'ascoltatore in frammento con l'attività live corrente. Questo risolve il problema.


0

Quello che ho scoperto è che se un'altra app è di tipo finestra di dialogo e consente l'invio di tocchi all'app in background, quasi ogni app in background andrà in crash con questo errore. Penso che dobbiamo verificare ogni volta che viene eseguita una transazione se l'istanza è stata salvata o ripristinata.


0

Nel mio caso, con la stessa eccezione di errore, ho inserito "onBackPressed ()" in un runnable (è possibile utilizzare qualsiasi vista):

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

Non capisco perché, ma funziona!


La pubblicazione su una vista eseguirà il Runnable solo dopo che la vista è stata correttamente disposta e disegnata sullo schermo; questo spesso significa che l'attività stessa è ripresa completamente, quindi nessun problema
Mercato

0

Potresti chiamare fragmentManager.popBackStackImmediate (); quando l'attività è in pausa. L'attività non è terminata ma è in pausa e non in primo piano. Devi verificare se l'attività è in pausa o meno prima di popBackStackImmediate ().


0

Ho notato qualcosa di molto interessante. Ho nella mia app l'opzione per aprire la galleria del telefono e il dispositivo chiede quale app utilizzare, lì faccio clic sull'area grigia lontano dalla finestra di dialogo e ho visto questo problema. Ho notato come la mia attività vada da onPause, onSaveInstanceState a onResume, non capita di visitare onCreateView. Sto effettuando transazioni su onResume. Quindi quello che ho finito per fare è impostare un flag negato su Pausa, ma essere vero su CreareaView. se il flag è true su Resume, allora fai onCommit, altrimenti commitAllowingStateLoss. Potrei andare avanti e perdere così tanto tempo, ma volevo controllare il ciclo di vita. Ho un dispositivo che è sdkversion 23 e non ho questo problema, ma ne ho un altro che è 21, e lì lo vedo.


-1

puoi usare FragmentActivity.onStart prima di popBackStackImmediate

come questo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.