Android - Impostazione di un timeout per un AsyncTask?


125

Ho una AsyncTaskclasse che eseguo che scarica un grande elenco di dati da un sito Web.

Nel caso in cui l'utente finale disponga di una connessione dati molto lenta o imprevedibile al momento dell'uso, vorrei effettuare il AsyncTasktimeout dopo un periodo di tempo. Il mio primo approccio a questo è così:

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 );

Dopo aver avviato il AsyncTask, viene avviato un nuovo gestore che annullerà AsyncTaskdopo 30 secondi se è ancora in esecuzione.

è un buon approccio? O c'è qualcosa AsyncTaskche è più adatto a questo scopo?


18
Dopo aver provato diversi approcci, ho concluso che la tua domanda dovrebbe essere la risposta accettata.
JohnEye,

Grazie per la domanda Mi sono davvero imbattuto nello stesso problema e il tuo pezzo di codice mi ha aiutato +1
Ahmad Dwaik 'Warlock'

1
La tua domanda è di per sé una buona risposta +50 :)
karthik kolanji il

Risposte:


44

Sì, c'è AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Nota che chiamando questo nel thread principale (AKA. UI thread) bloccherà l'esecuzione, probabilmente dovrai chiamarlo in un thread separato.


9
Sembra che sconfigge lo scopo dell'utilizzo di AsyncTask per iniziare se questo metodo di timeout viene eseguito sul thread dell'interfaccia utente principale ... Perché non usare semplicemente l' handlerapproccio che descrivo nella mia domanda? Sembra molto più semplice perché il thread dell'interfaccia utente non si blocca durante l'attesa per eseguire Handler's Runnable ...
Jake Wilson,

Ok grazie, non ero sicuro che ci fosse o meno un modo corretto di farlo.
Jake Wilson,

3
Non capisco perché chiami get () in un altro thread. Sembra strano perché AsyncTask stesso è una specie di thread. Penso che la soluzione di Jakobud sia più OK.
emeraldhieu

Penso che .get () sia simile al join del thread posix, in cui lo scopo è attendere il completamento del thread. Nel tuo caso funge da timeout, che è una proprietà circostanziale. Ecco perché devi chiamare .get () dal gestore o da un altro thread per evitare il blocco dell'interfaccia utente principale
Constantine Samoilenko,

2
So che è anni dopo, ma questo non è ancora integrato in Android, quindi ho creato una classe di supporto. Spero che aiuti qualcuno. gist.github.com/scottTomaszewski/…
Scott Tomaszewski

20

Nel caso, il tuo downloader si basi su una connessione URL, hai una serie di parametri che potrebbero aiutarti a definire un timeout senza codice complesso:

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

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Se porti semplicemente questo codice nel tuo compito asincrono, va bene.

Il "Timeout di lettura" consiste nel testare una rete difettosa lungo tutto il trasferimento.

'Timeout connessione' viene chiamato solo all'inizio per verificare se il server è attivo o meno.


1
Sono d'accordo. L'uso di questo approccio è più semplice e termina effettivamente la connessione al raggiungimento del timeout. Utilizzando l'approccio AsyncTask.get () ti viene solo comunicato che è stato raggiunto il limite di tempo, ma il download è ancora in fase di elaborazione e potrebbe effettivamente essere completato in un secondo momento, causando ulteriori complicazioni nel codice. Grazie.
stupito il

Si noti che ciò non è sufficiente quando si utilizzano molti thread su connessioni Internet molto lente. Maggiori informazioni: leakfromjavaheap.blogspot.nl/2013/12/… e thushw.blogspot.nl/2010/10/…
Kapitein Witbaard

20

Utilizzare la classe CountDownTimer a fianco della classe estesa per AsyncTask nel metodo onPreExecute ():

Vantaggio principale, il monitoraggio Async eseguito internamente nella classe.

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();
...

cambia CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000) per esempio e chiamerà onTick () 6 volte prima di chiamare onFinish (). Questo è utile se si desidera aggiungere un po 'di monitoraggio.

Grazie per tutti i buoni consigli che ho ricevuto in questa pagina :-)


2

Non credo che ci sia qualcosa di simile incorporato in AsyncTask. Il tuo approccio sembra essere buono. Assicurati di controllare periodicamente il valore di isCancelled () nel metodo doInBackground del tuo AsyncTask per terminare questo metodo una volta che il thread dell'interfaccia utente lo annulla.

Se vuoi evitare di usare il gestore per qualche motivo, puoi controllare System.currentTimeMillis periodicamente all'interno di AsyncTask e uscire in timeout, anche se mi piace di più la tua soluzione poiché può effettivamente interrompere il thread.


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();

         }

2
Prendi in considerazione di aggiungere anche qualche spiegazione
Saif Asif il

0

Puoi aggiungere un'altra condizione per rendere la cancellazione più solida. per esempio,

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

0

Ispirandomi alla domanda, ho scritto un metodo che esegue alcune attività in background tramite AsyncTask e se l'elaborazione richiede più di LOADING_TIMEOUT, verrà visualizzata una finestra di avviso per riprovare.

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.