Puedo descargar algunos datos de internet en el subproceso de fondo (yo uso AsyncTask) y mostrar un cuadro de diálogo de progreso mientras se descargan. Los cambios de orientación, la Actividad se reinicia y luego mi AsyncTask es completado, quiero despedir el progreso de diálogo y empezar una nueva Actividad. Pero las llamadas a dismissDialog a veces se produce una excepción (probablemente debido a que la Actividad fue destruida y la nueva Actividad no se ha iniciado aún).

¿Cuál es la mejor manera de manejar este tipo de problema (actualización de la interfaz de usuario de subproceso en segundo plano que funciona incluso si el usuario cambia de orientación)? ¿Alguien de Google proporcionar algunos «solución oficial»?

  • Mi blog sobre este tema podría ser de ayuda. Se trata de la retención de tareas de larga ejecución a través de los cambios de configuración.
  • Este pregunta está relacionado con así.
  • Sólo FTR hay una relacionada con el misterio aquí .. stackoverflow.com/q/23742412/294884
InformationsquelleAutor fhucho | 2010-09-29

8 Comentarios

  1. 335

    Paso #1: Hacer su AsyncTask un static clase anidada, o una completamente independiente de la clase, no sólo de un interior (no estático anidada) de la clase.

    Paso #2: Tener la AsyncTask aferrarse a la Activity a través de un miembro de datos, establecida a través del constructor y un setter.

    Paso #3: Cuando la creación de la AsyncTask, el suministro de la corriente Activity para el constructor.

    Paso #4: En onRetainNonConfigurationInstance(), retorno de la AsyncTask, después de separar del original, ahora-va-a distancia de la actividad.

    Paso #5: En onCreate(), si getLastNonConfigurationInstance() no es null, fundido a su AsyncTask clase y llame a su setter para asociar su nueva actividad con la tarea.

    Paso #6: no se refieren a los datos de la actividad de un miembro de doInBackground().

    Si sigues la receta anterior, se hará todo el trabajo. onProgressUpdate() y onPostExecute() están suspendidos entre el inicio de onRetainNonConfigurationInstance() y el final del siguiente onCreate().

    Aquí es un proyecto de ejemplo la demostración de la técnica.

    Otro enfoque es el de la zanja de la AsyncTask y mover su trabajo en un IntentService. Esto es particularmente útil si el trabajo a realizar puede ser larga, y deben ir en independientemente de lo que el usuario hace en términos de actividades (por ejemplo, la descarga de un archivo de gran tamaño). Usted puede usar una orden de emisión Intent a la actividad responde al trabajo que se realiza (si todavía está en el primer plano) o subir un Notification para que el usuario sepa si el trabajo ha sido realizado. Aquí hay un post en el blog con más información sobre este patrón.

    • Muchas gracias por tu gran respuesta a este problema común! Acaba de ser exhaustivo, se puede añadir al paso #4 que tenemos que separar (null) la actividad en el AsyncTask. Esto se ilustra bien en el proyecto de ejemplo, aunque.
    • Gaudin: Considerar editado! 🙂 Gracias!
    • Gracias, Marcos. Funciona perfektly para mí. Y gracias por la Muestra de Proyecto, sin ella no podría entender muy bien.
    • Gracias ayudó mucho. Cómo puede ser implementado con una vista de lista. Puede usted por favor revisen este stackoverflow.com/questions/4583737/
    • Estoy perplejo por esto – en su código de ejemplo, puede llamar a detach() en onRetainNonConfigurationInstance(). Este método no es llamado (supongo) cuando el usuario finihes la Actividad de la navegación por la espalda o cuando la Actividad es terminada por el sistema debido a la baja de memoria. ¿No es mejor detach() onDestroy()?
    • No quiero separar en esos casos. Por lo tanto, yo no separar en esos casos.
    • Más específicamente, esta es una muestra que cubre los cambios de configuración. No es una muestra que cubre mediados de ejecutar el cierre de la actividad o cualquier otra cosa. Si usted siente que usted quiere detach() en su propio código para el onDestroy() ruta, ir a la derecha por delante. En términos de por qué me separe en onRetainNonConfigurationInstance(), que es debido a que fue el primer punto cuando yo puede separar y seguir las reglas establecidas por la Señora Hackborn. Si la desconexión tiene sentido en otros escenarios (por ejemplo, onDestroy()) es hasta usted con ese escenario.
    • Pero, ¿y si necesito tener un acceso a la Actividad de los miembros?
    • ¿Qué pasa si su Actividad tiene varios tipos de tareas?
    • crear múltiples AsyncTask implementaciones
    • Estoy principalmente en referencia a la implementación de onRetainNonConfigurationInstance y llame a getLastNonConfigurationInstance. Parece que sólo 1 objeto puede ser pasado a través de.
    • Crear una clase interna estática o algo que se aferra a varios objetos, y la devuelva.
    • Gotcha :DDDDDDD
    • Este es ridículamente útil, gracias.
    • onRetainNonConfigurationInstance() está en desuso y la propuesta alternativa es el uso de setRetainInstance(), pero no devuelven un objeto. Es posible manejar asyncTask en cambio en la configuración con setRetainInstance()?
    • Absolutamente. Tiene la Fragment mantener la AsyncTask. Tiene la Fragment llamada setRetainInstance(true) sobre sí mismo. Tiene la AsyncTask sólo hablar con el Fragment. Ahora, en un cambio de configuración, el Fragment no es destruido y recreado (aunque la actividad es), y así la AsyncTask se conservan en el cambio de configuración.
    • es AsyncTaskLoader ahora la forma recomendada de hacerlo en lugar de la solución?
    • Sólo si usted está usando un ContentProvider de un Activity.
    • estoy tomando sobre AsynTaskLoader que puede cargar desde cualquier fuente y no un CursorLoader que las cargas de un ContentProvider.
    • Va a ser aún más útil si tienes que actualizar algunas reglas acerca de AsyncTaskLoaders así.
    • No, AsyncTaskLoader no es la forma recomendada de hacerlo porque no es lo mismo en todos. Un AsyncTask realiza una sola operación asincrónica. Un AsyncTaskLoader asincrónica cargas para un Activity y/o Fragment, retiene la carga de datos a través de cambios en la configuración y automáticamente realiza nueva carga cuando cambia a la fuente de datos son detectados. Si necesita realizar una sola, una sola vez, potencialmente costosa operación, entonces no tendría sentido utilizar un AsyncTaskLoader… uso un AsyncTask lugar.
    • Sólo una pregunta: ¿por Qué Paso #3: Cuando la creación de la AsyncTask, la oferta de la Actividad actual para el constructor ? ¿Por qué no Crear una interfaz IInterface y su aplicación en la Actividad / fragmento y, a continuación, simplemente llame a IInterface.SomeMethod() (que actualiza la Vista ) de onProgressUpdate ?
    • Debido a que el AsyncTask no tiene acceso a nada de implementar la interfaz, excepto a través de el paso #3.
    • Bien, he serio como este: no proporcionar la Actividad para el constructor directamente, sino sólo la interfaz de la actividad (o algún otro objeto) implementa. Se podría entonces decidir qué hacer, todo el mundo tiene su propia implementación. público MyAsyncTask(IMyInterface miinterfaz)
    • Usted es bienvenido a pasar en una implementación de la interfaz, si lo prefiere.
    • Yo uso el apoyo de la biblioteca y se ha anulado este método como el final! Doh!!!
    • Acabo de añadir una forma sencilla de evitar el uso obsoleto métodos, espero que sea aceptado
    • La edición debe ser una respuesta.
    • hmm, yo no tenía una copia de 🙁
    • Hola CW, si llegas a ver este comentario… Cuando se trae una pantalla completa a la espera de ruleta, he seguido este solución extremadamente simple: stackoverflow.com/a/9123421/294884 sin duda, hay una parte oculta de la desventaja no? Es «demasiado bueno para ser verdad»? THX
    • Desventajas se discuten en los comentarios a esta respuesta: stackoverflow.com/a/3614089/3558355
    • chuckB — GRACIAS
    • Esta solución era exactamente lo que necesitaba, pero como Indrek se mencionó anteriormente, onRetainNonConfigurationInstance() ahora es obsoleto e inútil en un AppCompatActivity. Vi que usted ha mencionado cómo implementar esto con setRetainInstance() en un Fragmento, pero hay una manera de sacar esto adelante sin incorporar el AsyncTask en un Fragmento, es decir, en un AppCompatActivity?
    • hay una manera de sacar esto adelante sin incorporar el AsyncTask en un Fragmento, es decir, en un AppCompatActivity?» – personalmente, yo sólo uso AsyncTask en conserva fragmento. Fuera de ese patrón, voy tenedor ordinaria de hilo y el uso de un bus de eventos (por ejemplo, greenrobot del EventBus) para obtener resultados de una actividad. Es posible que existen patrones para el éxito de la AsyncTask de uso que no implican conservan fragmentos, aunque no sé lo que son.
    • He encontrado una solución. AppCompatActivity tiene su propia versión especial de este método llamado onRetainCustomNonConfigurationInstance(). Yo era capaz de utilizar que con sus pasos de arriba y funcionó como se pretendía. Gracias por su respuesta.

  2. 13

    Aceptado la respuesta fue muy útil, pero no tiene un cuadro de diálogo de progreso.

    Afortunadamente para usted, lector, he creado una muy completo y ejemplo de trabajo de un AsyncTask con un diálogo de progreso!

    1. De rotación de las obras, y el diálogo sobrevive.
    2. Puede cancelar la tarea y de diálogo pulsando el botón atrás (si se desea este comportamiento).
    3. Se utiliza fragmentos.
    4. El diseño del fragmento debajo de los cambios de actividad correctamente cuando el dispositivo gira.
    • El aceptó respuesta es acerca de las clases estáticas (no miembros). Y los son necesarios para evitar que el AsyncTask tiene un (oculto) puntero al exterior de la instancia de la clase que se convierte en una pérdida de memoria en la destrucción de la actividad.
    • Sí, no sé por qué puse eso acerca de los miembros estáticos, ya que en realidad yo también… raro. Editado respuesta.
    • Podría usted por favor, actualice su enlace? Realmente necesito este.
    • Lo siento, no tiene en torno a la restauración de mi sitio web – voy a hacerlo pronto! Pero en el tiempo medio es básicamente el mismo que el código de esta respuesta: stackoverflow.com/questions/8417885/…
    • El enlace es falso; sólo conduce a un inútil índice con ninguna indicación de que el código es.
    • Lo siento, se ha cambiado el enlace para que apunte a la otra respuesta con todo el código.
    • Puede usted por favor enviar el contenido de tu blog dentro de tu respuesta? Los enlaces salientes son una especie de mal visto como una manera de responder.
    • Ya tengo…

  3. 9

    He trabajado durante una semana para encontrar una solución a este dilema, sin tener que recurrir a la edición del archivo de manifiesto. Los supuestos de esta solución son:

    1. Siempre es necesario utilizar un cuadro de diálogo de progreso
    2. Sólo una tarea que se realiza en un tiempo
    3. Que necesita la tarea a persistir cuando se gire el teléfono y el diálogo de progreso a ser automáticamente descarta.

    Aplicación

    Necesitará copiar los dos archivos que se encuentra en la parte inferior de este post en su área de trabajo. Sólo asegúrese de que:

    1. Todos sus Activitys debe extenderse BaseActivity

    2. En onCreate(), super.onCreate() debe ser llamado después de inicializar los miembros que necesitan tener acceso a su ASyncTasks. También, anular getContentViewId() para proporcionar el diseño del formulario de identificación.

    3. Reemplazar onCreateDialog() como de costumbre para crear diálogos administrado por la actividad.

    4. Ver el código a continuación para una muestra de una clase interna estática para hacer su AsyncTasks. Usted puede almacenar su resultado en mResult para acceder más tarde.


    final static class MyTask extends SuperAsyncTask<Void, Void, Void> {
    
        public OpenDatabaseTask(BaseActivity activity) {
            super(activity, MY_DIALOG_ID); //change your dialog ID here...
                                           //and your dialog will be managed automatically!
        }
    
        @Override
        protected Void doInBackground(Void... params) {
    
            //your task code
    
            return null;
        }
    
        @Override
        public boolean onAfterExecute() {
            //your after execute code
        }
    }

    Y, finalmente, el lanzamiento de su nueva tarea:

    mCurrentTask = new MyTask(this);
    ((MyTask) mCurrentTask).execute();

    Que es! Espero que esta solución robusta va a ayudar a alguien.

    BaseActivity.java (organizar las importaciones de sí mismo)

    protected abstract int getContentViewId();
    public abstract class BaseActivity extends Activity {
    protected SuperAsyncTask<?, ?, ?> mCurrentTask;
    public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(getContentViewId());
    mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
    if (mCurrentTask != null) {
    mCurrentTask.attach(this);
    if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
    && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
    mCurrentTask.postExecution();
    }
    }
    }
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
    super.onPrepareDialog(id, dialog);
    mDialogMap.put(id, true);
    }
    @Override
    public Object onRetainNonConfigurationInstance() {
    if (mCurrentTask != null) {
    mCurrentTask.detach();
    if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
    && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
    return mCurrentTask;
    }
    }
    return super.onRetainNonConfigurationInstance();
    }
    public void cleanupTask() {
    if (mCurrentTask != null) {
    mCurrentTask = null;
    System.gc();
    }
    }
    }

    SuperAsyncTask.java

    public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
    protected BaseActivity mActivity = null;
    protected Result mResult;
    public int dialogId = -1;
    protected abstract void onAfterExecute();
    public SuperAsyncTask(BaseActivity activity, int dialogId) {
    super();
    this.dialogId = dialogId;
    attach(activity);
    }
    @Override
    protected void onPreExecute() {
    super.onPreExecute();
    mActivity.showDialog(dialogId); //go polymorphism!
    }    
    protected void onPostExecute(Result result) {
    super.onPostExecute(result);
    mResult = result;
    if (mActivity != null &&
    mActivity.mDialogMap.get((Integer) dialogId) != null
    && mActivity.mDialogMap.get((Integer) dialogId)) {
    postExecution();
    }
    };
    public void attach(BaseActivity activity) {
    this.mActivity = activity;
    }
    public void detach() {
    this.mActivity = null;
    }
    public synchronized boolean postExecution() {
    Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
    if (dialogExists != null || dialogExists) {
    onAfterExecute();
    cleanUp();
    }
    public boolean cleanUp() {
    mActivity.removeDialog(dialogId);
    mActivity.mDialogMap.remove((Integer) dialogId);
    mActivity.cleanupTask();
    detach();
    return true;
    }
    }
  4. 4

    Hizo alguien de Google proporcionar algunos «solución oficial»?

    Sí.

    La solución es más de la arquitectura de aplicación de la propuesta en lugar de que sólo algunas código.

    Propusieron 3 patrones de diseño que permite a una aplicación para trabajar en sincronización con un servidor, independientemente del estado de la aplicación (funciona incluso si el usuario finaliza la aplicación, el usuario cambia de pantalla, la aplicación se termina, todo lo que sea posible estado donde un fondo de operación de datos podría ser interrumpted, esto lo cubre)

    La propuesta se explica en el Android RESTO de aplicaciones del cliente discurso durante Google I/O 2010 por Virgilio Dobjanschi. Es de 1 hora de duración, pero es extremadamente vale la pena ver.

    La base es la abstracción de las operaciones de la red a un Service que funciona de forma independiente a cualquier Activity en la aplicación. Si estás trabajando con bases de datos, el uso de ContentResolver y Cursor le daría un fuera-de-la-caja patrón Observer que es conveniente para actualizar la interfaz de usuario sin ningún tipo de adicional de la lógica, una vez que la actualización de su base de datos local con la traída de datos remoto. Cualquier otro después de la operación de código se ejecutan a través de una devolución de llamada pasa a la Service (yo uso un ResultReceiver subclase para esto).

    De todos modos, mi explicación es bastante vaga, usted debe definititely ver el discurso.

  5. 2

    Mientras que Marcos (CommonsWare) respuesta sí funciona para los cambios de orientación, se produce un error si la Actividad es destruido directamente (como en el caso de una llamada de teléfono).

    Usted puede manejar los cambios de orientación Y el raro destruido eventos de Actividad mediante el uso de un objeto de Aplicación para hacer referencia a un ASyncTask.

    Hay una excelente explicación del problema y la solución aquí:

    De crédito va completamente a Ryan para calcular esto.

  6. 1

    Después de 4 años de Google resuelto el problema, con sólo llamar a setRetainInstance(true) en el onCreate de la Actividad. Se preservará su instancia de la actividad durante la rotación del dispositivo. Tengo también una solución simple para Android más antiguos.

    • El problema de la gente observador sucede porque Android destruye una actividad de clase en la rotación, extensión del teclado y otros eventos, pero asincronica tarea todavía mantiene una referencia para la destruida instancia e intenta utilizarlo para las actualizaciones de IU. Usted puede instruir a Android para no destruir la actividad, ya sea en un manifiesto o pragmáticamente. En este caso asincronica tarea de referencia sigue siendo válido y no hay problema observado. Desde la rotación pueden requerir algún trabajo adicional como recargar puntos de vista y así sucesivamente, Google no recomienda para preservar la actividad. Así que usted decide.
    • Gracias, yo sabía acerca de la situación, pero no se trata de setRetainInstance(). Lo que no entiendo es su afirmación de que Google utiliza esta para resolver las cuestiones que se pide en la pregunta. Puede que el enlace de la fuente de la información? Gracias.
    • developer.android.com/reference/android/app/…
    • onRetainNonConfigurationInstance() se llama a Esta función puramente como una optimización, y usted no debe confiar en lo que se llama. < a partir De la misma fuente: developer.android.com/reference/android/app/…
  7. 0

    usted debe llamar a todos los de la actividad acciones a través de la actividad de controlador. Así que si usted está en un hilo, se debe crear un Ejecutable y publicado mediante Activida del Controlador. De lo contrario, su aplicación se bloqueará a veces con la excepción fatal.

Dejar respuesta

Please enter your comment!
Please enter your name here