web-dev-qa-db-fra.com

"Impossible d'effectuer cette action après onSaveInstanceState". Pourquoi cette exception est-elle générée par la méthode onResume de mon activité?

Mon activité appelle la caméra avec l'intention ACTION_IMAGE_CAPTURE. Si l'activité de la caméra revient avec succès, je mets un indicateur dans le rappel onActivityResult et, en fonction de la valeur de l'indicateur, je commence un fragment dans mon onResume pour ajouter une légende à l'image capturée. Cela semble fonctionner correctement. 

Je viens de recevoir une trace de pile du "wild" se plaignant que j'essayais de valider une transaction de fragment après l'appel de onSaveInstanceState. Mais je fais le commit dans ma méthode onResume! Pourquoi Android s'en plaindrait-il? J'ai Android: configChanges = "orientation | keyboardHidden | keyboard | screenSize" défini dans mon fichier AndroidManifest.xml, de sorte qu'un changement d'orientation ne devrait pas déclencher cela ....

Cela s’est produit sur un Samsung Galaxy S3 (SGH-i747) fonctionnant sous 4.0.4.

Voici la pile:

Java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at Android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.Java:1314)
    at Android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.Java:1325)
    at Android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.Java:548)
    at Android.support.v4.app.BackStackRecord.commit(BackStackRecord.Java:532)
    at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.performFragmentTransition(AddPhotosActivity2.Java:278)
    at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.switchToCaptionsFragment(AddPhotosActivity2.Java:438)
    at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.onResume(AddPhotosActivity2.Java:167)
    at Android.app.Instrumentation.callActivityOnResume(Instrumentation.Java:1158)
    at Android.app.Activity.performResume(Activity.Java:4544)
    at Android.app.ActivityThread.performResumeActivity(ActivityThread.Java:2448)
    at Android.app.ActivityThread.handleResumeActivity(ActivityThread.Java:2486)
    at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1187)
    at Android.os.Handler.dispatchMessage(Handler.Java:99)
    at Android.os.Looper.loop(Looper.Java:137)
    at Android.app.ActivityThread.main(ActivityThread.Java:4514)
    at Java.lang.reflect.Method.invokeNative(Native Method)
    at Java.lang.reflect.Method.invoke(Method.Java:511)
    at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:980)
    at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:747)
    at dalvik.system.NativeStart.main(Native Method)

Toute aide ou sagesse est appréciée.

28
PacificSky

Je pense je connais la réponse - J'utilise la bibliothèque de compatibilité FragmentActivity from v4 et je dois donc effectuer mes transactions de fragment dans onResumeFragments au lieu de onResume. Quelqu'un peut confirmer?

54
PacificSky

vous pouvez utiliser la méthode commitAllowingStateLoss()

mais sachez que vous pouvez perdre l’état de votre activitéComme vous pouvez le voir dans la référence de Google Android , qui explique la différence entre les deux de la manière suivante

Identique à commit () mais permet l'exécution de la validation après l'enregistrement de l'état d'une activité. Cela est dangereux car la validation peut être perdue si l'activité doit être restaurée ultérieurement, elle ne doit donc être utilisée que dans les cas où l'état de l'interface utilisateur peut changer de manière inattendue pour l'utilisateur.

d'après mon expérience, la méthode addToBackStack peut ne pas fonctionner parfois, vous devrez donc l'ajouter manuellement sur le fragment Bien entendu, l'état ne sera pas enregistré (textbox text ext.)

12
Asaf Manassen

cela a fonctionné pour moi ... je l'ai découvert par moi-même ... j'espère que cela vous aidera!

1) ne PAS avoir un FragmentManager/FragmentTransaction "statique" global.

2) onCreate, TOUJOURS initialiser à nouveau FragmentManager!

échantillon ci-dessous: -

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

Mise à jour Je pense avoir trouvé une explication et une solution ici: http://code.google.com/p/Android/issues/detail?id=23096#c4 J'ai implémenté le La solution de contournement de Fragment vide a été postée ici et n’a plus d’exception IllegalStateException pour l’instant.

J'ajoute le fragment d'état invisible à mon activité comme ceci;

@Override
protected void onCreate(final Bundle args) {
    ...
    if (args == null) {
        final FragmentManager fm = this.getSupportFragmentManager();
        final FragmentTransaction ft = fm.beginTransaction();
        final Fragment emptyFragmentWithCallback = new EmptyFragmentWithCallbackOnResume();
        ft.add(emptyFragmentWithCallback, EmptyFragmentWithCallbackOnResume.TAG);
        ft.commit();
    }

Le code suivant provient du lien ci-dessus:

public class EmptyFragmentWithCallbackOnResume extends Fragment {
OnFragmentAttachedListener mListener = null;

@Override
public void onAttach(SupportActivity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentAttachedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnFragmentAttachedListener");
    }
}

@Override
public void onResume() {
    super.onResume();
    if (mListener != null) {
        mListener.OnFragmentAttached();
    }
}

public interface OnFragmentAttachedListener {
    public void OnFragmentAttached();
}
}

et invoquer des transactions de fragment qui iraient intuitivement dans onResume ou onResumeFragments dans ma méthode personnalisée onFragmentAttached qui est appelée par le fragment d'état invisible. Je n'utilise pas du tout onResumeFragments et n'émet aucune transaction de fragment dans onResume.

Donc, pour résumer. Si vous utilisez le support lib et fragments, oubliez quasiment onResume, oubliez onResumeFragments et implémentez votre propre "onResume" basé sur la solution de contournement ci-dessus . C'est un peu ridicule. 


Je ne peux pas confirmer. J'ai exactement le même problème. bien que j'émette une transaction de fragment dans onResumeFragments . Cela fonctionnait comme je l'ai posté ici: IllegalStateException - Bibliothèque de support de fragment

Il semble que l'erreur ne se produise que sur les versions 4.0.3 et 4.0.4. Cependant, cela ne se produit pas toujours ni dans mon émulateur.

J'utilise support lib rev. 10 et API 16 . J'appelle DialogFragment.show dans onResumeFragments et reçois continuellement cette exception ridicule de certains utilisateurs aléatoires. Je ne peux pas le reproduire localement. 

2
phlebas

J'ai d'abord développé mon application ciblant Android 2.2 (SDK 8), à l'aide de la bibliothèque de support technique v4, et lorsque j'ai commencé à l'utiliser avec la version 4.2 (SDK 17), j'avais les mêmes problèmes avec mes fragments. Mais changer mon manifeste pour Android: minSdkVersion = "8" Android: targetSdkVersion = "17", a résolu mes problèmes. Peut-être que ça te va aussi.

1
Andre Rocha

J'obtenais toujours cela quand j'essayais de montrer un fragment dans la méthode onActivityForResult (), alors le problème était le suivant:

  1. Mon activité est suspendue et arrêtée, ce qui signifie que onSaveInstanceState () a déjà été appelé (pour les dispositifs antérieurs à Honeycomb et postérieurs à Honeycomb).
  2. En cas de résultat, j'ai effectué une transaction pour afficher/masquer un fragment, ce qui provoque cette exception IllegalStateException.

Ce que j'ai fait est ensuite:

  1. Valeur ajoutée pour déterminer si l'action souhaitée a été effectuée (par exemple, prendre une photo à partir d'un camere - isPhotoTaken) - il peut s'agir d'une valeur booléenne ou entière, en fonction du nombre de transactions différentes dont vous avez besoin.
  2. Dans la méthode onResumeFragments () overriden, j'ai vérifié ma valeur et, après avoir effectué les transactions de fragments dont j'avais besoin. Dans ce cas, commit () n'a pas été exécuté après onSaveInstanceState, car state a été renvoyé dans la méthode onResumeFragments ().
1
Array

Ajoutez ceci à votre activité:

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