android android-fragments android-dialogfragment android-permissions android-6.0-marshmallow

android - Llamar a DialogFragment''s show() desde dentro onRequestPermissionsResult() causa IllegalStateException en Marshmallow



android-fragments android-dialogfragment (7)

Pasos:

  1. Solicite un permiso de Fragment o Activity
  2. Mostrar un DialogFragment desde onRequestPermissionsResult()
  3. java.lang.IllegalStateException se lanza: No se puede realizar esta acción después de onSaveInstanceState

Esto no ocurre cuando aparece el diálogo después de un retraso (usando postDelayed). De acuerdo con http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html ) en dispositivos post-Honeycomb PODEMOS commit() entre onPause() y onStop() sin ninguna PÉRDIDA DE ESTADO o EXCEPCION. Aquí hay un enlace al origen del proyecto de muestra, archivo de registro y problema registrado. https://drive.google.com/folderview?id=0BwvvuYbQTUl6STVSZF9TX2VUeHM&usp=sharing

También abrí un problema https://code.google.com/p/android/issues/detail?id=190966 pero estaba marcado como WorkingAsIntended y sugieren capturar excepciones. Pero esto no resuelve el problema. Conozco otras formas de resolverlo, pero ¿no es este error de Android?

ACTUALIZACIÓN Estado para que el error sea nuevamente "asignado". Espero que se solucione pronto. Mi solución temporal es

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


Como está utilizando un DialogFragment , debe mantener un indicador o estado y luego mostrar su diálogo onPostResume . Debería hacer esto en onPostResume en lugar de onResume u otros métodos de ciclo de vida.

Como la documentation establece claramente, si realiza transacciones en los métodos del ciclo de vida de la actividad que no sean onPostResume o onResumeFragments (para FragmentActivity), en algunos casos se puede llamar al método antes de que se haya restaurado completamente el estado de la actividad.

Entonces, si muestras tu diálogo en PostResume, estarás bien.


Como se explicó en uno de los comentarios del https://code.google.com/p/android/issues/detail?id=190966 , esto solo ocurre cuando se usa el DialogFragment la lib de DialogFragment (es decir, android.support.v4.app.DialogFragment ). Puede cambiar su código para usar el DialogFragment "nativo" ( android.app.DialogFragment ) y funcionará DialogFragment .

Sé que en algunos casos no es posible utilizar el DialogFragment nativo, pero en este caso, dado que te refieres a los permisos de tiempo de ejecución (una función que solo está disponible en las últimas versiones de Android), lo más probable es que puedas hacerlo. Tenía este problema exacto y podría solucionarlo de esta manera.


El error se acepta y se solucionará, sin embargo, estoy totalmente en desacuerdo con las soluciones postDelayed y el temporizador. La mejor forma de hacerlo sería introducir un indicador de estado en la Actividad, en el que configura la devolución de llamada y usar en Repetición o similar. Por ejemplo:

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

Y luego en onResume:

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



Solucioné esto en lo que creo que es una forma mucho más determinista. En lugar de usar temporizadores, básicamente pongo el resultado en cola hasta Activity.onResumeFragments () (o puedes hacerlo en Activity.onResume () si no estás usando fragmentos). Lo hice en ResumeFragments () porque también enruto el resultado al fragmento específico que solicitó el permiso, así que necesito estar seguro de que los fragmentos están listos.

Aquí está el 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; } }

Luego, aquí está onResumeFragments ():

@Override protected void onResumeFragments() { super.onResumeFragments(); mFragmentsResumed = true; if (mQueuedPermissionGrantResults != null) { routePermissionsResult(mQueuedPermissionRequestCode, mQueuedPermissions, mQueuedPermissionGrantResults); }

Y para completar, aquí está onPause (), que borra el indicador mFragmentsResumed:

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

Utilicé el indicador mFragmentsResumed porque no quiero que este código deje de funcionar si google soluciona el error (cambiar el orden de las invocaciones del ciclo de vida haría que el código no funcionara si simplemente establecía las variables en cola en onRequestPermissionsResult pero onResumeFragments era llamado antes de esto).


También creo que esto es un error de Android. No puedo creer que hayan marcado su problema WorkingAsIntended . La única solución por el momento es retrasar la ejecución del código en onRequestPermissionsResult() hasta que los onRequestPermissionsResult() Android arreglen esto correctamente.

Esta es mi forma de resolver este problema si alguien se pregunta cómo retrasar la ejecución:

@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == PERMISSION_CODE) { if (/* PERMISSION ALLOWED/DENIED */) { new Timer().schedule(new TimerTask() { @Override public void run() { // EXECUTE ACTIONS (LIKE FRAGMENT TRANSACTION ETC.) } }, 0); } }

Esto esencialmente demora la ejecución hasta que onRequestPermissionsResult() para que no obtengamos java.lang.IllegalStateException . Esto funciona en mi aplicación.


intente algo como esto:

// ... 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*/; } }