Android AsyncTask no se detendrá cuando se cancele, ¿por qué?
android-asynctask (5)
Busqué más y recordé que mi hilo estaba llamando a un método mal escrito que contenía esto, produciendo otro hilo dentro del que necesito cancelar:
public static void haveASleep(int milliseconds)
{
try
{
Thread.sleep(milliseconds);
}
catch (InterruptedException ie)
{
Trace.w(TAG,"Sleep interrupted");
}
}
Entonces, lo que sucedió fue que el subproceso de AsyncTask llama a este método para esperar un tiempo, y durante el reposo, la llamada al método cancel () se produce en el AsyncTask, lo que causa una excepción en el reposo del subproceso. Mi código capturó y absorbió inútilmente esa excepción, en lugar de usarla para cerrar la AsyncTask.
La solución fue buscar una excepción en el modo de espera y, si se lanza, salir silenciosamente del hilo. La aplicación ahora funciona como se esperaba, PERO ...
El estado inmediatamente después de la llamada al método cancel () sigue EN EJECUCIÓN, pero sospecho que esto se debe a que en ese momento todavía se está ejecutando y el sistema operativo aún no lo ha cerrado. No tengo idea si eso es cierto, pero esa es mi mejor estimación.
Tengo una AsyncTask que cierro en el evento del ciclo de vida onPause de la Actividad, por lo que no se ejecuta cuando alguien abandona la aplicación, pero continúa a pesar de esto. Agregué algo de rastreo y este fragmento muestra el problema.
Trace.d(TAG,"Task state: " + myTask.getStatus() );
myTask.cancel(true);
Trace.d(TAG,"Task state: " + myTask.getStatus() );
Salidas:
Task state: RUNNING
Task state: RUNNING
¿Por qué el método cancel () no tiene ningún efecto sobre el estado de la tarea? Me doy cuenta de que los documentos dicen que el método de cancelación "intentará" detener la tarea, pero ¿en qué circunstancias fallará? La tarea se está ejecutando definitivamente, ya que genera una salida de registro cada diez segundos y, como puede ver, su estado se devuelve como en ejecución.
Actualización: agregué el rastreo para mostrarme el estado isCancelled () también y eso SÍ cambia. Por lo tanto, la llamada a cancelar (verdadero) está cambiando el estado cancelado de falso a verdadero, pero aparentemente no tiene efecto en el estado, o detiene el hilo.
Incluso si sabes cómo funciona la cancelación, es muy difícil determinar en qué estado se detendrá tu hilo. Por lo tanto, sugiero implementar su propia forma segura de detener su hilo, para estar seguro de en qué estado se está deteniendo y asegurarse de que no haya pérdidas de memoria, etc. Más información sobre cómo detener un hilo en Java de forma segura way check this thread ¿Cómo abortar un hilo de forma rápida y limpia en java?
Otro posible escollo para verificar si te encuentras con este problema:
Asegúrese de que no está iniciando dos AsyncTasks.
Tenga en cuenta que su Actividad y su AsyncTask son dos hilos separados. Por lo tanto, incluso si cancela la AsyncTask, el código para cancelar la tarea puede que aún no se ejecute. Es por eso que ves que AsyncTask sigue funcionando incluso después de corregir tu error. No creo que el estado cambie hasta que haya doInBackground()
en su AsyncTask. (¡Así que asegúrate de estar marcando isCancelled()
y regresando temprano cuando hayas sido cancelado!)
thread.cancel(true);
Sólo establece una variable en la clase AsyncTask. Lo que debe hacer es implementar un bucle en su tarea en segundo plano si aún no lo ha hecho. Luego, verifique y rompa el ciclo si la cancelación es verdadera para cada iteración.
@Override
protected Void doInBackground(Void... params) {
synchronized(this) {
for(int i = 0; i < 9000; i++) {
if(thread.isCancelled()) break;
// do stuff
}
}
}
Luego, configúrelo como cancelado siempre que lo necesite para detenerlo. Entonces se detendrá antes de la siguiente iteración.
@Override
public void onStop() {
super.onStop();
thread.cancel(true);
}
@Override
public void onDestroy() {
super.onDestroy();
thread.cancel(true);
}