Cómo manejar AsyncTask onPostExecute cuando se pausan para evitar IllegalStateException

Aprecio las numerosas publicaciones sobre AsyncTask en un cambio de rotación. Tengo el siguiente problema al usar el lib de la compatibilidad y de intentar descartar un DialogFragment en onPostExecute .

Tengo un fragmento que se activa de un AsyncTask que muestra un progreso DialogFragment , a continuación, en onPostExecute descarta el diálogo y, a continuación, potencialmente lanza otro DialogFragment .

Si cuando el diálogo del progreso se está exhibiendo puse la aplicación en el fondo consigo el siguiente para mi fragmento:

1) onPause

2) onSaveInstanceState

3) onPostExecute en el que intento descartar e invocar un diálogo.

Recibo una IllegalStateException porque estoy tratando de comprometer efectivamente una transacción cuando la actividad ha guardado su estado y entiendo esto.

En una rotación he asumido (quizás incorrectamente) que no conseguiría un onPostExecute hasta que la actividad se ha recreado. Sin embargo, al poner la aplicación en segundo plano asumí (definitivamente incorrectamente) que el onPostExectute no sería llamado mientras el fragmento / actividad estaba pausado.

Mi pregunta es, ¿es mi solución simplemente para detectar en onPostExecute que el fragmento / actividad está en pausa y simplemente realizar lo que tengo que hacer en onResume en onResume lugar? Me parece algo feo.

Gracias de antemano, Peter.

Editar 1

Necesidad de soporte 2.1 y superior

Editar 2

He considerado la posibilidad de mostrar el diálogo utilizando FragmentTransaction:add y FragmentTransaction:commitAllowingStateLoss sin embargo esto no es sin problemas.

Si necesita sincronizar su tarea con el ciclo de vida de la actividad, creo que los cargadores son exactamente lo que necesita. Más específicamente, debe utilizar AsyncTaskLoader para realizar el trabajo. Así que ahora en lugar de ejecutar un AsyncTask, se inicia su cargador, a continuación, esperar a la respuesta en un oyente. Si la actividad está en pausa, no obtendrá una devolución de llamada, esta parte se administrará para usted.

Hay otra manera de manejar esta tarea: usando un fragmento que retiene su instancia . La idea general es crear un fragmento sin UI y llamar a setRetainInstance(true) . Tiene una tarea que está siendo notificada sobre la actividad que está disponible o no. De lo contrario, el subproceso de la tarea se suspende hasta que una actividad esté disponible.

Otra forma de lograr lo que necesita es implementar la clase PauseHandler que documenté en esta publicación .

A continuación, en su método onPostExecute, envíe sendMessage () para enviar su mensaje al manejador.

Cuando su aplicación se reanude, la acción será manejada.

En lugar de usar BroadcastReceiver, prefiero usar bibliotecas de bus como guava, otto o eventbus. Su rendimiento es mucho mejor que la implementación del receptor de difusión.

Me surgió una solución para este problema sin ninguna solución importante: La idea básica de cómo mantener un progressdialog y un asynctask se describe en este blogentry (por supuesto que he utilizado el AsyncTaskComplex-Version). Todos los créditos van al autor de este blog, solo agregué una pequeña cosa:

Obviamente no estoy usando showDialog () más. En su lugar me quedo con DialogFragments.

El segundo tweak es el importent y también resuelve el problema con la IllegalStateException:

En lugar de solo decirle al asynctask en onRetainCustomNonConfigurationInstance () que ya no hay actividad, también lo hago en onPause (). Y en lugar de solo decirle al asynctask en onCreate () que hay una nueva actividad, también lo hago en onResume ().

Y allí usted va, su AsyncTask no intentará informar su actividad sobre su final que causa un IllegalStateException cuando la actividad no es visible.

Si desea ver más código en lugar de palabras, deje un comentario.

/ Edit: Sourcecode para mostrar mi solución, que creo que es bastante decente 🙂

 public class MyActivity extends Activity { private MyTask mTask; @Override protected void onCreate(Bundle pSavedInstanceState) { super.onCreate(pSavedInstanceState); setContentView(R.layout.editaccount); Object retained = getLastCustomNonConfigurationInstance(); if ( retained instanceof NewContactFolderIdTask ) { mTask = (MyTask) retained; mTask.setActivity(this); } } @Override protected void onPause() { if(mTask != null) { mTask.setActivity(null); } super.onPause(); } @Override public Object onRetainCustomNonConfigurationInstance() { if(mTask != null) { mTask.setActivity(null); return mTask; } return null; } @Override protected void onResume() { if(mTask != null) { mTask.setActivity(this); } loadValues(); // or refreshListView or whatever you need to do super.onResume(); } public void onTaskCompleted() { loadValues(); // or refreshListView or whatever you need to do DialogFragment dialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(PROGRESS_DIALOG_FRAGMENT); if(dialogFragment != null) { dialogFragment.dismiss(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater menuInflater = getMenuInflater(); menuInflater.inflate(R.menu.main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // app icon in Action Bar clicked; go home Intent intent = new Intent(this, OXClient.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return true; case R.id.menu_refresh: mTask = new MyTask(this); mTask.execute(); break; } return super.onOptionsItemSelected(item); } private class NewContactFolderIdTask extends AsyncTask<Boolean, Integer, Bundle> { private MyActivity mActivity; private boolean mCompleted; private NewContactFolderIdTask(MyActivity pActivity) { this.mActivity = pActivity; } public void setActivity(MyActivity pActivity) { this.mActivity = pActivity; if(mCompleted) { notifiyActivityTaskCompleted(); } } private void notifiyActivityTaskCompleted() { if(mActivity != null) { mActivity.onTaskCompleted(); } } @Override protected Bundle doInBackground(Boolean... pBoolean) { // Do your stuff, return result } @Override protected void onPreExecute() { DialogFragment newFragment = ProgressDialogFragment.newInstance(); newFragment.show(getSupportFragmentManager(), PROGRESS_DIALOG_FRAGMENT); } @Override protected void onPostExecute(Bundle pResult) { mCompleted = true; notifiyActivityTaskCompleted(); } } 

}

En Cómo manejar los mensajes del manejador cuando se interrumpe la actividad / fragmento , ofrezco otra aproximación usando BroadcastReceiver.

Lo considero más limpio y elegante y ofrece las ventajas de que puede invocar código en su fragmento base desde cualquier lugar de su aplicación y mediante emisiones pegajosas su invocación puede ser "recordada" y ejecutada después de que su fragmento se reanude.

  • La aplicación se reinicia en lugar de continuar
  • ¿Cómo saber cuándo se ha matado mi aplicación?
  • Cuándo usar y cuándo no utilizar un servicio en Android
  • Android: Cuando se cancela un servicio, ¿cómo podemos persistir el estado del servicio para una restauración posterior?
  • Diferencia entre onPause y onStop ()
  • cómo comprobar si una actividad implementa una interfaz después de onAttach (Activity activity) ha sido depreciada
  • Mal comportamiento de Backstack de la actividad cuando la actividad destruida
  • Los miembros de la clase en Fragmento se convierten en nulos después de pulsar el botón Inicio y esperar
  • Transición de la actividad: Realización de la parada de la actividad que no se reanuda
  • Actividad y Fragmento Ciclos de Vida y Cambios de Orientación
  • Distinguir entre presionar el botón "inicio" y abrir otra Actividad
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.