Android - Définition d'un délai d'expiration pour une AsyncTask?

125

J'ai une AsyncTaskclasse que j'exécute qui télécharge une grande liste de données à partir d'un site Web.

Dans le cas où l'utilisateur final a une connexion de données très lente ou irrégulière au moment de l'utilisation, j'aimerais que le AsyncTaskdélai d'attente soit écoulé après un certain temps. Ma première approche est la suivante:

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

Après avoir démarré le AsyncTask, un nouveau gestionnaire est démarré qui annulera les AsyncTask30 secondes après s'il est toujours en cours d'exécution.

Est-ce une bonne approche? Ou y a-t-il quelque chose de AsyncTaskmieux adapté à cette fin?

Jake Wilson
la source
18
Après avoir essayé plusieurs approches différentes, j'ai conclu que votre question devrait être la réponse acceptée.
JohnEye
Merci pour la question, je suis vraiment tombé dans le même problème et votre morceau de code m'a aidé +1
Ahmad Dwaik 'Warlock'
1
Votre question est en elle-même une bonne réponse +50 :)
karthik kolanji

Réponses:

44

Oui, il existe AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Notez qu'en appelant ceci dans le thread principal (AKA. UI thread) bloquera l'exécution, vous devrez probablement l'appeler dans un thread séparé.

Yorkw
la source
9
On dirait que cela va à l'encontre de l'objectif d'utiliser AsyncTask pour commencer si cette méthode d'expiration s'exécute sur le thread principal de l'interface utilisateur ... Pourquoi ne pas utiliser simplement l' handlerapproche que je décris dans ma question? Cela semble beaucoup plus simple car le thread de l'interface utilisateur ne se fige pas en attendant d'exécuter le gestionnaire d'exécution ...
Jake Wilson
D'accord merci, je n'étais tout simplement pas sûr qu'il y avait une bonne façon de le faire ou non.
Jake Wilson
3
Je ne comprends pas pourquoi vous appelez get () dans un autre thread. Cela semble étrange car AsyncTask lui-même est une sorte de thread. Je pense que la solution de Jakobud est plus OK.
emeraldhieu
Je pense que .get () est similaire à la jointure de thread posix, où le but est d'attendre la fin du thread. Dans votre cas, il sert de délai d'attente, qui est une propriété circonstancielle. C'est pourquoi vous devez appeler .get () à partir du gestionnaire ou d'un autre thread pour éviter le blocage principal de l'interface utilisateur
Constantine Samoilenko
2
Je sais que c'est des années plus tard, mais ce n'est toujours pas intégré à Android, alors j'ai créé une classe de support. J'espère que cela aide quelqu'un. gist.github.com/scottTomaszewski/…
Scott Tomaszewski
20

Dans le cas où votre téléchargeur est basé sur une connexion URL, vous disposez d'un certain nombre de paramètres qui pourraient vous aider à définir un délai d'expiration sans code complexe:

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

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Si vous apportez simplement ce code dans votre tâche asynchrone, c'est ok.

«Read Timeout» consiste à tester un mauvais réseau tout au long du transfert.

'Connection Timeout' n'est appelé qu'au début pour tester si le serveur est opérationnel ou non.

Clément Soullard
la source
1
Je suis d'accord. L'utilisation de cette approche est plus simple et met fin en fait à la connexion lorsque le délai d'expiration est atteint. En utilisant l'approche AsyncTask.get (), vous êtes seulement informé que la limite de temps a été atteinte, mais le téléchargement est toujours en cours de traitement et peut en fait se terminer plus tard - ce qui entraîne plus de complications dans le code. Merci.
étourdi le
Veuillez noter que cela ne suffit pas lorsque vous utilisez beaucoup de threads sur des connexions Internet très lentes. Plus d'infos: leakfromjavaheap.blogspot.nl/2013/12/… et thushw.blogspot.nl/2010/10
Kapitein Witbaard
20

Utilisez la classe CountDownTimer à côté de la classe étendue pour AsyncTask dans la méthode onPreExecute ():

Principal avantage, la surveillance Async effectuée en interne dans la 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();
...

changez CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000) par exemple et il appellera onTick () 6 fois avant d'appeler onFinish (). C'est bien si vous souhaitez ajouter une surveillance.

Merci pour tous les bons conseils que j'ai reçus sur cette page :-)

Gilco
la source
2

Je ne pense pas qu'il y ait quelque chose comme ça intégré à AsyncTask. Votre approche semble être la bonne. Assurez-vous simplement de vérifier périodiquement la valeur de isCancelled () dans la méthode doInBackground de votre AsyncTask pour mettre fin à cette méthode une fois que le thread d'interface utilisateur l'a annulée.

Si vous souhaitez éviter d'utiliser le gestionnaire pour une raison quelconque, vous pouvez vérifier périodiquement System.currentTimeMillis dans votre AsyncTask et quitter à l'expiration du délai, bien que j'aime mieux votre solution car elle peut en fait interrompre le thread.

aleph_null
la source
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
la source
2
Pensez également à ajouter des explications
Saif Asif
0

Vous pouvez mettre une condition supplémentaire pour rendre l'annulation plus robuste. par exemple,

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

Inspiré de la question, j'ai écrit une méthode qui effectue une tâche d'arrière-plan via AsyncTask et si le traitement prend plus de LOADING_TIMEOUT, un dialogue d'alerte pour réessayer apparaîtra.

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
la source