Android: ¿establecer un tiempo de espera para una AsyncTask?

125

Tengo una AsyncTaskclase que ejecuto que descarga una gran lista de datos de un sitio web.

En el caso de que el usuario final tenga una conexión de datos muy lenta o irregular en el momento del uso, me gustaría dejar el AsyncTasktiempo de espera después de un período de tiempo. Mi primer acercamiento a esto es así:

MyDownloader downloader = new MyDownloader();
downloader.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
  @Override
  public void run() {
      if ( downloader.getStatus() == AsyncTask.Status.RUNNING )
          downloader.cancel(true);
  }
}, 30000 );

Después de iniciar AsyncTask, se inicia un nuevo controlador que cancelará AsyncTaskdespués de 30 segundos si aún se está ejecutando.

¿Es este un buen enfoque? ¿O hay algo integrado AsyncTaskque sea más adecuado para este propósito?

Jake Wilson
fuente
18
Después de probar varios enfoques diferentes, llegué a la conclusión de que su pregunta debería ser la respuesta aceptada.
JohnEye
Gracias por la pregunta, realmente caí en el mismo problema y su código me ayudó a +1
Ahmad Dwaik 'Warlock'
1
Su pregunta es en sí misma una buena respuesta +50 :)
karthik kolanji

Respuestas:

44

Sí, hay AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Tenga en cuenta que al llamar a esto en el hilo principal (AKA. UI thread) bloqueará la ejecución, probablemente necesite llamarlo en un hilo separado.

yorkw
fuente
9
Parece que frustra el propósito de usar AsyncTask para comenzar si este método de tiempo de espera se ejecuta en el hilo principal de la interfaz de usuario ... ¿Por qué no usar el handlerenfoque que describo en mi pregunta? Parece mucho más sencillo porque el hilo de la interfaz de usuario no se congela mientras espera ejecutar el Runler ejecutable del manejador ...
Jake Wilson
Bien, gracias, no estaba seguro de si había una forma adecuada de hacerlo o no.
Jake Wilson el
3
No entiendo por qué llamas get () en otro hilo. Parece extraño porque AsyncTask en sí es una especie de hilo. Creo que la solución de Jakobud está más bien.
emeraldhieu
Creo que .get () es similar a la unión de hilos posix, donde el propósito es esperar a que se complete el hilo. En su caso, sirve como un tiempo de espera, que es una propiedad circunstancial. Es por eso que debe llamar a .get () desde el controlador o desde otro hilo para evitar el bloqueo de la interfaz de usuario principal
Constantine Samoilenko
2
Sé que esto es años después, pero todavía no está integrado en Android, así que hice una clase de soporte. Espero que ayude a alguien. gist.github.com/scottTomaszewski/…
Scott Tomaszewski
20

En el caso, su descargador se basa en una conexión URL, tiene una serie de parámetros que podrían ayudarlo a definir un tiempo de espera sin código complejo:

  HttpURLConnection urlc = (HttpURLConnection) url.openConnection();

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Si solo lleva este código a su tarea asincrónica, está bien.

'Tiempo de espera de lectura' es probar una red defectuosa durante toda la transferencia.

'Tiempo de espera de conexión' solo se llama al principio para probar si el servidor está activo o no.

Clement Soullard
fuente
1
Estoy de acuerdo. Usar este enfoque es más simple, y en realidad termina la conexión cuando se alcanza el tiempo de espera. Al usar el enfoque AsyncTask.get (), solo se le informa que se ha alcanzado el límite de tiempo, pero la descarga aún se está procesando y, de hecho, puede completarse más adelante, lo que causa más complicaciones en el código. Gracias.
aturdido el
Tenga en cuenta que esto no es suficiente cuando se utilizan muchos hilos en conexiones a Internet muy lentas. Más información: leakfromjavaheap.blogspot.nl/2013/12/… y thushw.blogspot.nl/2010/10/…
Kapitein Witbaard
20

Use la clase CountDownTimer junto a la clase extendida para AsyncTask en el método onPreExecute ():

Ventaja principal, la supervisión asíncrona realizada internamente en la clase.

public class YouExtendedClass extends AsyncTask<String,Integer,String> {
...
public YouExtendedClass asyncObject;   // as CountDownTimer has similar method -> to prevent shadowing
...
@Override
protected void onPreExecute() {
    asyncObject = this;
    new CountDownTimer(7000, 7000) {
        public void onTick(long millisUntilFinished) {
            // You can monitor the progress here as well by changing the onTick() time
        }
        public void onFinish() {
            // stop async task if not in progress
            if (asyncObject.getStatus() == AsyncTask.Status.RUNNING) {
                asyncObject.cancel(false);
                // Add any specific task you wish to do as your extended class variable works here as well.
            }
        }
    }.start();
...

cambie CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000) por ejemplo y llamará a onTick () 6 veces antes de llamar a onFinish (). Esto es bueno si desea agregar algo de monitoreo.

Gracias por todos los buenos consejos que recibí en esta página :-)

Gilco
fuente
2

No creo que haya algo así integrado en AsyncTask. Su enfoque parece ser bueno. Solo asegúrese de verificar periódicamente el valor de isCancelled () en el método doInBackground de su AsyncTask para finalizar este método una vez que el hilo de la interfaz de usuario lo cancele.

Si desea evitar usar el controlador por alguna razón, puede verificar System.currentTimeMillis periódicamente dentro de su AsyncTask y salir en el tiempo de espera, aunque me gusta su solución, ya que en realidad puede interrumpir el hilo.

aleph_null
fuente
0
         Context mContext;

         @Override
         protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
                                    mContext = this;

            //async task
            final RunTask tsk = new RunTask (); 
            tsk.execute();

            //setting timeout thread for async task
            Thread thread1 = new Thread(){
            public void run(){
                try {
                    tsk.get(30000, TimeUnit.MILLISECONDS);  //set time in milisecond(in this timeout is 30 seconds

                } catch (Exception e) {
                    tsk.cancel(true);                           
                    ((Activity) mContext).runOnUiThread(new Runnable()
                    {
                         @SuppressLint("ShowToast")
                        public void run()
                         {
                            Toast.makeText(mContext, "Time Out.", Toast.LENGTH_LONG).show();
                            finish(); //will close the current activity comment if you don't want to close current activity.                                
                         }
                    });
                }
            }
        };
        thread1.start();

         }
Mrityunjaya
fuente
2
Considere agregar alguna explicación también
Saif Asif
0

Puede poner una condición más para hacer que la cancelación sea más sólida. p.ej,

 if (downloader.getStatus() == AsyncTask.Status.RUNNING || downloader.getStatus() == AsyncTask.Status.PENDING)
     downloader.cancel(true);
Vinnig
fuente
0

Inspirando a partir de la pregunta, he escrito un método que realiza algunas tareas en segundo plano a través de AsyncTask y si el procesamiento lleva más de LOADING_TIMEOUT, aparecerá un diálogo de alerta para volver a intentarlo.

public void loadData()
    {
        final Load loadUserList=new Load();
        loadUserList.execute();
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (loadUserList.getStatus() == AsyncTask.Status.RUNNING) {
                    loadUserList.cancel(true);
                    pDialog.cancel();
                    new AlertDialog.Builder(UserList.this)
                            .setTitle("Error..!")
                            .setMessage("Sorry you dont have proper net connectivity..!\nCheck your internet settings or retry.")
                            .setCancelable(false)
                            .setPositiveButton("Retry", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialogInterface, int i) {
                                    loadData();
                                }
                            })
                            .setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                                @Override


                      public void onClick(DialogInterface dialogInterface, int i) {
                                System.exit(0);
                            }
                        })
                        .show();
            }
        }
    }, LOADING_TIMEOUT);
    return;
}
Abhishek
fuente