2011-10-21 13 views
6

Estoy trabajando con una situación bastante común en este momento: descargar algunos datos en la web y luego actualizar una vista para mostrarlos. Claramente, quiero hacer la descarga web en segundo plano, y luego actualizar la vista en el hilo principal de UI. Ahora, al mirar mi código, estoy un poco preocupado de que mi actividad y sus elementos de IU se eliminen antes de actualizarlos. Aquí está la esencia de lo que tengo en mente:¿Cómo puedo saber si mi contexto aún es válido?

Thread update = new Thread() { 
    public void run() { 
     final Data newData = requestData();      
     if (newData != null) { 
      post(new Runnable() { 
       public void run() { 
        Toast.makeText(MyClass.this, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show(); 
       } 
      }); 
     } 
    } 
}; 
update.start(); 

Parece posible que mientras estoy descargando los datos, la actividad puede ser destruido. ¿Qué pasa entonces? ¿Mi hilo continuará ejecutándose? ¿Terminaré tratando de acceder a los objetos muertos?

Normalmente hago esto por AsycTask, pero esta vez el trabajo me pareció lo suficientemente simple como para simplemente poner en línea el tema hilos-lanzar-hilos. ¿Haré las cosas mejor usando una AsyncTask?

Respuesta

2

Si utiliza clases anónimas, tendrán una referencia interna a la clase externa, por lo que no es posible que se vuelva inaccesible de repente porque se borraron otras referencias. AsyncTask en realidad no cambia nada, utiliza mecanismos similares para notificar acerca de los resultados.

Puede usar loaders, están diseñados para estar sincronizados con el ciclo de vida de la actividad. Están disponibles solo desde Android 3.0, pero puede usar support package para trabajar con ellos en cualquier dispositivo con 1.6 o posterior.

Hay incluso una solución más simple, puede simplemente usar un campo booleano que indica si la actividad se ha ido. Debe establecer este campo en onPause() (o cuando crea que ya no necesitará las notificaciones) y verifíquelo cuando muestre brindis. Ni siquiera tendrá que usar la sincronización, ya que este campo está limitado al hilo principal, por lo que es absolutamente seguro. Por cierto, si cambia este campo en otro lugar que no sea onDestroy(), no olvide agregar una declaración que restablece su campo en el método homólogo.

public class MyActivity extends Activity { 
    private boolean activityDestroyed = false; 

    @Override 
    protected void onDestroy() { 
     activityDestroyed = true; 
    } 

    private void updateData() { 
     new Thread() { 
      @Override 
      public void run() { 
       final Data newData = requestData();      
       if (newData == null) return;            

       runOnUiThread(new Runnable() { 
        public void run() { 
         if (activityDestroyed) return; 
         Toast.makeText(MyActivity.this, "Blah", 
           Toast.LENGTH_SHORT).show(); 
        } 
       }); 
      } 
     }.start(); 
    } 
} 
+0

Esto probablemente no compilará, ya sea que deba hacerlo estático, o debe anteponer MyActivity.this. – EboMike

+0

Copié principalmente el código de la pregunta solo para dar una idea general, no pretendía que el código de la respuesta se usara para las aplicaciones reales. Actualicé el código, ahora compilará (aunque esto no es realmente importante). – Malcolm

+0

Voy a probar este booleano por ahora, así que le doy a éste la marca de verificación. Aún así, gracias por sus respuestas, Kurtis y EboMike. Todo lo que no había oído hablar de Cargadores hasta que los mencionaste. – MaximumGoat

6

Lo que realmente quiere usar es un AsyncTaskLoader. Estas son mis nuevas clases favoritas en la API de Android. Los uso todo el tiempo y fueron hechos para resolver problemas como este. No tendrá que preocuparse por cuándo detener su descarga ni nada de eso. Toda la lógica de enhebrado se ocupa de usted, incluso decirle al hilo que se detenga si la actividad se ha cerrado. Simplemente diga qué es lo que quiere hacer en el método loadInBackground(). Tenga en cuenta que si está desarrollando una API inferior a 3.0, todavía puede acceder a todos los cargadores a través del Android Support Package.

+2

Esta declaración acerca de las discusiones en realidad imprecisa, que no se detienen o incluso interrumpido a menos les dices que lo hagan en tu cargador.Lo que realmente ocurre de manera predeterminada es que el oyente para los resultados de la tarea simplemente se elimina de los cargadores cuando se reinician. Es por eso que no recibirá una devolución de llamada en su fragmento o actividad una vez completada la tarea, pero no se cancelará. – Malcolm

+0

Ah, está bien. Bueno saber. –

0

Kurtis tiene razón. Sin embargo, si usted realmente quiere que sea sencillo, puede intentar esto:

class MyActivity extends Activity { 
    static MyActivity context; 

    @Override 
    public void onCreate(Bundle icicle) { 
     super.onCreate(icicle); 

     MyActivity.context = this; 
    } 

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

     MyActivity.context = null; 
    } 
} 

Y entonces sólo tiene que utilizar MyActivity.context en su clase (y comprobar si hay nula allí). Si desea que el brindis no se muestre cuando su aplicación está en segundo plano, use onPause/onResume en su lugar.

De nuevo, este es el enfoque rápido y perezoso. AsyncTask o AsyncTaskLoader es cómo debería estar haciendo las cosas.

+0

Creo que la comprobación de [isDestroyed()] (http://developer.android.com/reference/android/app/Activity.html#isDestroyed()) en su instancia de Activity es más simple? –

+0

Esto causará pérdida de memoria si establece una referencia estática a la actividad –

11

Si su Context es un Activity, se puede comprobar si se está terminando o ha terminado con el isFinishing() método:

if (context instanceof Activity) { 
    Activity activity = (Activity)context; 
    if (activity.isFinishing()) { 
     return; 
    } 
} 
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show(); 
+0

Gracias. La solución perfecta para mí. – hybrid

Cuestiones relacionadas