web-dev-qa-db-fra.com

Impossible d'effectuer cette action après le spectacle onSaveInstanceState

Je reçois les rapports d'accident suivants:

Fatal Exception: Java.lang.IllegalStateException: impossible d'exécuter cette action après onSaveInstanceState sur Android.support.v4.app.FragmentManagerImpl.checkStateLoss (FragmentManager.Java:1832) sur Android.support.v4.app.FragmentManagerImpl.enQ. Java: 1850) sur Android.support.v4.app.BackStackRecord.commitInternal (BackStackRecord.Java:643) sur Android.support.v4.app.BackStackRecord.commit (BackStackRecord.Java:643) sur Android.support.v4.app.BackStackRecord.commit (BackStackRecord.Java:603) sur Android.support.v4.app .DialogFragment.show (DialogFragment.Java:143)

Le code suivant est à l’origine du crash, j’ai coupé du code de configuration trivial pour plus de clarté. J'ai lu cette erreur et, si j'ai bien compris, la .show() devrait être en sécurité dans le cadre d'une interaction utilisateur telle que onClick(). La seule chose à laquelle je peux penser, c'est que la query() prend beaucoup de temps et que l'utilisateur le remplace. Est-ce une explication sensée de cette erreur? C'est instantané sur mes appareils, même avec une base de données volumineuse. Toute autre possibilité? Merci!

    foldersButton.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            final List<String> paths = new ArrayList<>();

            try(Cursor c = getActivity().getContentResolver().query(Meta.CONTENT_URI,
                    new String[]{"DISTINCT " + Meta.PARENT}, null, null,
                    Meta.PARENT + " ASC"))
            {
                while (c != null && c.moveToNext())
                {
                    String path = c.getString(c.getColumnIndex(Meta.PARENT));

                    // We place the excluded folders at the end
                    if (!mExcludedFolders.contains(path))
                        paths.add(path);
                }
            }

            [setup...]

            int[] position = new int[2];
            foldersButton.getLocationOnScreen(position);
            FolderDialog dialog = FolderDialog.newInstance(
                    paths.toArray(new String[paths.size()]),
                    visible,
                    excluded,
                    position[0],
                    position[1]);
            dialog.setStyle(DialogFragment.STYLE_NO_TITLE, R.style.FolderDialog);

            [setup...]

            dialog.show(getFragmentManager(), TAG);
        }
    });
5
Anthony

Si votre code se trouve dans une activité, encapsulez l'appel à show avec:

if(!isDestroyed() && !isFinishing())

et si ce code est dans un fragment, encapsulez l'appel à show avec:

if (isResumed() && !isRemoving())

Cela résoudrait plus ou moins le problème de l'atterrissage dans un état d'interface utilisateur incohérent.

6
Dibzmania

Plus je le regardais, plus cela me gênait que je pose une requête dans le code de l'interface utilisateur. C'était probablement mon code initial pour tester le concept et je l'ai laissé négligemment lorsqu'il a fonctionné.

Il existe une LoaderManager pour l'application qui utilise la mauvaise requête, mais j'ai utilisé la onLoadFinished existante pour lancer une requête asynchrone qui mettra à jour la structure de chemin d'accès lors de toute modification de la base de données. Désormais, lorsque l'utilisateur clique sur le bouton, la boîte de dialogue est prête à fonctionner.

Étant donné que je ne peux pas reproduire l'erreur, il faudra environ une semaine avant de pouvoir rendre un verdict sur celui-ci, mais dans tous les cas, le code précédent était bâclé.

0
Anthony

Il semble que votre activité soit en pause/que votre application ne soit pas dans le foregraound. Vous pouvez faire une chose avant d’ajouter un fragment. Vérifiez si votre activité n’est pas détruite/terminée et si votre application est au premier plan (elle n’est pas minimisée)

if(!isDestroyed()&&!isFinishing()&&isAppInForground(this)){
//add your fragment
}

Écrivez le code pour vérifier si votre application est au premier plan ou non

 /**
         *
         * @param context
         * @return boolean
         * tells whether user is currently viewing app or not if not return false otherwise true
         */
        public static boolean isAppInForground(Context context){
            boolean isInForeground = false;
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

                List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
                for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
                    if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        for (String activeProcess : processInfo.pkgList) {
                            if (activeProcess.equals(context.getPackageName())) {
                                isInForeground = true;
                            }
                        }
                    }
                }

            return isInForeground;
        }
0
Mansuu....

Vous pouvez afficher une boîte de dialogue de progression jusqu'à l'exécution de votre requête et faire en sorte que l'utilisateur attende à l'écran tout en l'informant qu'un traitement est en cours.

Ou si vous ne souhaitez pas informer l'utilisateur du traitement dans ce cas, vérifiez si l'activité est encore visible au premier plan, puis affichez la boîte de dialogue.

Vous pouvez utiliser une variable booléenne isInForeground et la définir/réinitialiser dans onResume et onPause respectivement.

 @Override
    protected void onPause() {
        super.onPause();
       isInForeground=false;
    }

    @Override
    protected void onResume() {
        super.onResume();
       isInForeground=true;
    }

Puis, affichez la logique de dialogue dans condition si

if(isInForeground){
  dialog.show(getFragmentManager(), TAG);
}
0
Nayan Srivastava