web-dev-qa-db-fra.com

L'appel de show () de DialogFragment depuis onRequestPermissionsResult () provoque IllegalStateException dans Marshmallow.

Pas:

  1. Demander la permission de Fragment ou Activity
  2. Afficher un DialogFragment à partir de onRequestPermissionsResult()
  3. Java.lang.IllegalStateException est levé: impossible d'exécuter cette action après onSaveInstanceState

Cela ne se produit pas lorsque je montre le dialogue après un certain délai (avec postDelayed) . Selon http://www.androiddesignpatterns.coms.com/13/08/fragment-transaction-commit-state-loss.html ) sur les appareils post-Honeycomb, nous POUVONS commit() entre onPause() et onStop() sans aucune perte d’État ni EXCEPTION. Voici un lien vers un exemple de source de projet, de fichier journal et de problème enregistré . https://drive.google.com/folderview?id=0BwvvuYbQTUl6STVSZF9TX2VUeHM&usp=sharing

De plus, j'ai ouvert un problème https://code.google.com/p/Android/issues/detail?id=190966 mais il a été marqué comme étant WorkingAsIntended et suggère d'attraper une exception. Mais cela ne résout pas le problème. Je connais d'autres moyens de le résoudre, mais n'est-ce pas ce bug d'Android?

UPDATE Le statut du bogue est à nouveau "assigné". J'espère que ça sera corrigé bientôt . Ma solution temporaire est 

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // do your fragment transaction here
    }
}, 200);
35
android_dev

Le bogue est accepté et sera corrigé. Cependant, je suis tout à fait en désaccord avec les solutions postDelayed et timer. La meilleure façon de procéder consiste à introduire un indicateur d'état dans l'activité, dans lequel vous définissez le rappel et que vous utilisez dans onResume ou similaire. Par exemple:

private boolean someFlag;

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// some code checking status
    someFlag = true;
}

Et puis dans onResume:

protected void onResume() {
    if(someFlag == true) {
        doSomething();
        someFlag = false;
    }
}
24
Kenneth

Je pense aussi que c'est un bug Android. Je ne peux pas croire qu'ils aient marqué votre problème WorkingAsIntended . La seule solution pour l’instant est de retarder l’exécution du code dans onRequestPermissionsResult() jusqu’à ce que les utilisateurs d’Android corrigent ce problème correctement.

C'est ma façon de résoudre ce problème si quelqu'un se demande comment retarder l'exécution:

 @Override public void onRequestPermissionsResult (int requestCode, permissions String [], int [] grantResults) {

 if (requestCode == PERMISSION_CODE) {
 si (/ * AUTORISATION AUTORISÉE/REFUSÉE * /) {
 new Timer (). schedule (new TimerTask () {
 @Override public void run () {
 // EXECUTER DES ACTIONS (SEMBLE UNE TRANSACTION PAR FRAGMENT, ETC.)
 } 
 } 

Cela retarde essentiellement l'exécution jusqu'à la fin de onRequestPermissionsResult() pour que nous n'ayons pas Java.lang.IllegalStateException. Cela fonctionne dans mon application.

6
ldulcic

Puisque vous utilisez une DialogFragment, vous devez conserver un drapeau ou un état et afficher votre dialogue dans onPostResume. Vous devriez le faire avec onPostResume plutôt que onResume ou d’autres méthodes du cycle de vie. 

Comme l'indique clairement le documentation , si vous validez des transactions dans des méthodes de cycle de vie d'activité autres que onPostResume ou onResumeFragments (pour FragmentActivity), dans certains cas, la méthode peut être appelée avant que l'état de l'activité ne soit entièrement restauré.

Donc, si vous affichez votre dialogue surPostResume, tout ira bien.

4
CanC

essayez quelque chose comme ça:

// ...
private Runnable mRunnable;

@Override
public void onResume() {
   super.onResume();

   if (mRunnable != null) {
       mRunnable.run();
       mRunnable = null;
   }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
   super.onRequestPermissionsResult(requestCode, permissions, grantResults);

   if (/* PERMISSION_DENIED */) {
      mRunnable = /* new Runnable which show dialogFragment*/;
   }
}
4
Scott Key

Il s'agit d'un bogue dans Android https://code.google.com/p/Android/issues/detail?id=190966&q=label%3AReportedBy-Developer&colspec=ID%20Type%20Status%20Owner%20Owner%20Summary%20Stars

Donc, pour le moment, des solutions de contournement sont nécessaires. Vous pouvez utiliser la solution de @ ldulcic. Ou vous pouvez utiliser la méthode postDelay de Handler. La deuxième option est préférée.

J'ai contourné ce problème de manière beaucoup plus déterministe à mon avis. Plutôt que d’utiliser des minuteries, j’ai essentiellement mis le résultat en attente jusqu’à Activity.onResumeFragments () (ou vous pouvez le faire dans Activity.onResume () si vous n’utilisez pas de fragments). J'ai utilisé onResumeFragments () car je renvoie également le résultat au fragment spécifique qui a demandé l'autorisation; je dois donc être sûr que les fragments sont prêts.

Voici le onRequestPermissionsResult ():

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    // This is super ugly, but google has a bug that they are calling this method BEFORE
    // onResume... which screws up fragment lifecycle big time.  So work around it.  But also
    // be robust enough to still work if/when they fix the bug.
    if (mFragmentsResumed) {
        routePermissionsResult(requestCode, permissions, grantResults);
    } else {
        mQueuedPermissionGrantResults = grantResults;
        mQueuedPermissionRequestCode = requestCode;
        mQueuedPermissions = permissions;
    }
}

Alors voici onResumeFragments ():

@Override
protected void onResumeFragments() {
    super.onResumeFragments();

    mFragmentsResumed = true;

    if (mQueuedPermissionGrantResults != null) {
        routePermissionsResult(mQueuedPermissionRequestCode, mQueuedPermissions,
                mQueuedPermissionGrantResults);
    }

Et pour être complet, voici onPause (), qui efface l'indicateur mFragmentsResumed:

@Override
protected void onPause() {
    super.onPause();

    mFragmentsResumed = false;
}

J'ai utilisé l'indicateur mFragmentsResumed car je ne veux pas que ce code cesse de fonctionner si et quand Google corrige le bogue (modifier l'ordre des invocations du cycle de vie ferait en sorte que le code ne fonctionne pas si je définissais simplement les variables en file d'attente dans onRequestPermissionsResult mais onResumeFragments appelé avant cela).

2
pdub

Je l'ai également résolu avec un gestionnaire, mais j'ai pu éviter l'utilisation d'une minuterie réglant son Looper sur MainLooper. Donc, il sera exécuté une fois la vue rendue:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // do your fragment transaction here
    }
});
0
Rubén Viguera

Comme expliqué dans l'un des commentaires de la question issue , cela ne se produit que lorsque vous utilisez la DialogFragment de la bibliothèque de support (c'est-à-dire, Android.support.v4.app.DialogFragment). Vous pouvez changer votre code pour utiliser la variable "native" DialogFragment (Android.app.DialogFragment) et cela fonctionnera correctement.

Je sais que dans certains cas, l'utilisation de la variable DialogFragment native n'est pas possible, mais dans ce cas, étant donné que vous faites référence aux autorisations d'exécution (une fonctionnalité disponible uniquement dans les dernières versions d'Android), vous êtes probablement en mesure de le faire. J'ai eu ce problème exact et je pourrais le résoudre de cette façon.

0
Franco