2011-07-11 18 views
15

Tengo AsyncTask que procesa algunas cosas de fondo HTTP. AsyncTask se ejecuta según lo programado (Alarmas/servicio) y en algún momento el usuario lo ejecuta manualmente.Android AsyncTask: evitar instancias múltiples ejecutando

Procedo los registros de SQLite y noté las publicaciones dobles en el servidor que me dice que algunas veces la tarea programada se ejecuta y al mismo tiempo el usuario la ejecuta manualmente, haciendo que se lea y procese el mismo registro desde DB dos veces. Elimino los registros después de que se procesaron pero sigo obteniendo esto.

¿Cómo debo manejarlo? Tal vez organizar algún tipo de queing?

Respuesta

15

Puede ejecutar sus AsyncTask de en una Executor usando executeOnExecutor()

Para asegurarse de que los hilos se están ejecutando en una forma de serie por favor uso: SERIAL_EXECUTOR.

Otra información: How to use an Executor

Si varias actividades están accediendo a su base de datos por qué no crear una especie de ayudante de base de datos de puerta de enlace y utilizar el bloque synchronized que exista un sólo hilo tiene acceso a ella en un instante

0

intente tener algún valor booleano de instancia que se establece en "verdadero" en el preejecutar asynctask y luego "falso" en postexecute. Luego, tal vez en Doinbackground compruebe si ese booleano es verdadero. en ese caso, llame a cancelar en esa tarea duplicada en particular.

+1

Me temo que si uso el indicador en la aplicación y mi AsyncTask falla por alguna razón - de lo que nunca se ejecutará nuevamente ya que la bandera ya muestra que comenzó – katit

6

Inicializa AsyncTask en nulo. Solo crea uno nuevo si es nulo. En onPostExecute, configúrelo como nulo nuevamente, al final. Haga lo mismo en onCancelado, en caso de que el usuario cancele esto. Aquí hay un código no probado para ilustrar la idea básica.

import android.app.Activity; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.view.View; 
import android.widget.Button; 

public class FooActivity extends Activity { 

    private class MyAsyncTask extends AsyncTask<Foo, Foo, Foo> { 
     @Override 
     protected void onPostExecute(Foo foo) { 
        // do stuff 
      mMyAsyncTask = null; 
     } 

     @Override 
     protected void onCancelled() { 
      // TODO Auto-generated method stub 
      mMyAsyncTask = null; 
     } 

     @Override 
     protected Foo doInBackground(Foo... params) { 
        try { 
         // dangerous stuff       
        } catch (Exception e) { 
         // handle. Now we know we'll hit onPostExecute() 
        } 

      return null; 
     } 
    } 

    private MyAsyncTask mMyAsyncTask = null; 

    @Override 
    public void onCreate(Bundle bundle) { 
     Button button = (Button) findViewById(R.id.b2); 
     button.setOnClickListener(new View.OnClickListener() { 

      public void onClick(View v) { 
       if (mMyAsyncTask == null) { 
        mMyAsyncTask = new MyAsyncTask(); 
        mMyAsyncTask.execute(null); 
       } 
      } 

     }); 
    } 

}

+0

Probé todas las demás soluciones pero seguía recibiendo db excepción no abierta al intentar acceder db dentro de una AsyncTask y esto es lo que funcionó para mí. Gracias –

+0

Gracias por una muy buena solución. –

10

O bien, puede probar este para ver si la tarea se está ejecutando actualmente o no:

if (katitsAsyncTask.getStatus().equals(AsyncTask.Status.FINISHED)) 
    katitsAsyncTask.execute(); 
else 
    // wait until it's done. 
+0

No funcionará. No guardo referencia a mi AsyncTask y me llamaron desde diferentes actividades y desde el servicio – katit

+0

. En realidad, también corro el mío en mis servicios. Mi Servicio conserva la referencia y no comenzará una nueva hasta que la existente haya finalizado. Simplemente guardo una cola de Actividades que desean usarla y ejecutarlas en serie a través de la lista, pero solo una invocación a la vez ... funciona. – BonanzaDriver

+0

Mi servicio no se ejecuta todo el tiempo, lo ejecuto usando Alarmas. Puedo usar banderas "duras" en la Base de datos, pero me temo que si AsyncTask muere por algún motivo y no actualiza esta bandera, nunca se ejecutará otra. Puedo hacer la marca de tiempo y declarar AsyncTask "muerto" si se está ejecutando durante un cierto período de tiempo, pero esta solución no me gusta. – katit

0

se podía mantener el estado de la tarea en las preferencias compartidas. Verifique el valor (booleano tal vez) antes de comenzar la tarea. Establecer el estado de acabado (verdad?) En onPostExecute y falso en OnPreExecute o en el constructor

1

Sé que esto fue hace un tiempo, y tú has resuelto tu problema. pero acabo de tener un problema similar. La sugerencia de Reno me puso en el camino correcto, pero para aquellos que han tenido dificultades para llenar los vacíos. Aquí es cómo superé un problema similar al de katit.

Quería que una AsyncTask en particular se ejecutara solo si no se estaba ejecutando actualmente. Y como un avance de la sugerencia de Reno, la interfaz AsyncTask se ha creado para manejar todos los procesos esenciales en el tratamiento adecuado de los hilos para Android. Lo que significa que el Ejecutor está integrado. Como esto blog sugiere:

"Cuando se ejecuta execute (Object .. params) en una AsyncTask, la tarea se ejecuta en una cadena de fondo. Dependiendo de la plataforma, AsyncTasks se puede ejecutar en serie (pre 1.6 y potencialmente de nuevo en 4+), o concurrentemente (1.6-3.2).

Para estar seguro de ejecutar en serie o al mismo tiempo como lo requiera, desde API Nivel 11 en adelante puede usar executeOnExecutor (Executor executor, Object .. params) método en su lugar, y el suministro de un ejecutor. La plataforma proporciona dos ejecutores para mayor comodidad, accesible como AsyncTask.SERIAL_EXECUTOR y AsyncTask.THREAD_POOL_EXECUTOR respectivamente. "

Así que con esto en mente, usted puede hacer hilo de bloqueo a través de la interfaz AsyncTask, también implica que puede simplemente usar el AsyncTasks.getStatus() para manejar el bloqueo de rosca, como se sugiere en este Deev post.

en mi código, he conseguido esto mediante:

Creación de una variable global definida como:

private static AsyncTask<String, Integer, String> mTask = null; 

Y en onCreate, inicializar como una instancia de mi AsyncTask llamada CalculateSpecAndDraw:

mTask = new CalculateAndDrawSpec(); 

Ahora, cuando cada vez que deseo llamar a este AsyncTask que rodean la ejecución de lo siguiente:

if(mTask.getStatus() == AsyncTask.Status.FINISHED){ 
      // My AsyncTask is done and onPostExecute was called 
      mTask = new CalculateAndDrawSpec().execute(Integer.toString(progress)); 
     }else if(mTask.getStatus() == AsyncTask.Status.PENDING){ 
      mTask.execute(Integer.toString(progress)); 
     }else{ 
      Toast.makeText(PlaySpecActivity.this, "Please Wait..", 1000).show(); 

     } 

Esto genera un nuevo hilo si se ha terminado, o si el estado de rosca está pendiente, el el hilo está definido pero no se ha iniciado, lo iniciamos. De lo contrario, si el hilo se está ejecutando no lo volvemos a ejecutar, simplemente le informamos al usuario que no está terminado o realizamos cualquier acción que deseemos. Luego, si desea programar el próximo evento en lugar de simplemente bloquearlo para que vuelva a estar en ejecución, consulte la documentación de this sobre el uso de ejecutores.

0

¿Qué tal envolver su check-lo-a-enviar y envía- que la lógica en un método synchronized? Este enfoque parece funcionar para nosotros.

Cuestiones relacionadas