web-dev-qa-db-fra.com

Vérifier si l'état est sauvegardé avant de valider une FragmentTransaction

Je vois parfois le stacktrace suivant pour un commit qui peut arriver quand l'utilisateur ne regarde pas l'activité (après que l'état ait été sauvegardé):

Java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at Android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.Java:1327)
    at Android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.Java:1338)
    at Android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.Java:595)
    at Android.support.v4.app.BackStackRecord.commit(BackStackRecord.Java:574)

En regardant la source Android, cela prend tout son sens:

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

Maintenant, je me demande s’il est possible (en plus de stocker une variable de classe dans (Sauvegarder/Restaurer) InstanceState) de vérifier si un fragment va être validé dans un état indésirable. le commit au moment opportun. 

39
hwrdprkns

Puisque vous n'avez pas attaché de code d'exemple, tout ce que je peux deviner, c'est que vous utilisez une "mauvaise" méthode lors de la validation de la transaction.

ainsi, au lieu d'utiliser FragmentTransaction.commit(), vous devriez utiliser FragmentTransaction.commitAllowingStateLoss ()

De plus, il existe des rapports et des solutions de contournement concernant ce problème (ou plutôt un changement de comportement de l'API) dans this blog de Google .

20
Tomo

A partir de la version de la bibliothèque de support 26.0.0 Beta 1 une nouvelle API est disponible dans les classes FragmentManager et Fragment:

FragmentManager et Fragment ont une méthode isStateSaved () permettant de demander si une transaction sera autorisée ou non sans perte d'état. Ceci est particulièrement utile pour vérifier lors de la gestion d'un événement onClick () avant d'exécuter une transaction.

D'après les documents de Android.support.v4.app.FragmentManager#isStateSaved() :

Renvoie true si l'état de FragmentManager a déjà été enregistré par son hôte. Aucune opération susceptible de modifier l'état enregistré ne doit pas être effectuée si cette méthode renvoie true. Par exemple, toute méthode popBackStack (), telle que popBackStackImmediate () ou FragmentTransaction utilisant commit () au lieu de commitAllowingStateLoss () modifiera l'état et entraînera une erreur.

Cette API sera livrée avec la structure Android.app.FragmentManager à partir d’Android O.

35
azizbekian

Malheureusement, Android Fragment ne fournit pas de vérification de l’API pour vérifier si la transaction est valide. 

Mais nous pouvons ajouter un champ booléen à l'activité attachée nous aidant à vérifier. Veuillez vous référer au code suivant.

public class GlobalBaseActivity extends FragmentActivity {

    private boolean mAllowCommit;

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

        mAllowCommit = true;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mAllowCommit = false;
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onResumeFragments() {
        mAllowCommit = true;
        super.onResumeFragments();
    }

    public boolean allowFragmentCommit() {
        return mAllowCommit;
    } 

    public void callbackOnEvent() {
        if (allowFragmentCommit()){
            getFragmentManager().beginTransaction().add(new YourFragment(), TAG).commit();
        }
    }
}

Pourquoi choisir onResumeFragment() comme indicateur de transaction, réf ce bon blog . Il explique la fameuse IllegalStateException en détail.

13
peacepassion

RuntimeExceptions (et IllegalStateException en est un) signifie généralement que votre code est incorrect quant à la façon dont il a tenté de réaliser quelque chose. Essayer de gérer une telle exception (par exemple en l'attrapant) est également une erreur. Votre code ne devrait jamais se comporter de la même façon qu'Android lève une exception comme celle-ci sur vous.

Si vous utilisez des gestionnaires et postez ou envoyez des messages à gérer ultérieurement, vous devez effacer la file d'attente avant de sortir de l'état de reprise . de votre activité. Vous devez l'annuler et vérifier s'il a été annulé.

Il existe de nombreux exemples comme ceux-ci et tous sont causés par des fuites de mémoire "temporaires", comme j'aime les appeler.

En gros, votre code est mauvais et sans le fournir, il est impossible de dire comment.

0
MaciejGórski

Vous ne devez appeler la méthode commit que dans ces méthodes OnCreate, OnResumeFragments, OnPostResume. Détails que vous pouvez lire ici Fragment Transactions & Loss State Activity

0
Abbath

ft.commitAllowingStateLose() est votre meilleur choix dans cette situation. Cependant, comme indiqué, rien ne garantit que votre état va persister. 

0
astryk