Android - Ein Timeout für eine AsyncTask festlegen?

125

ich habe ein AsyncTask Klasse, die ich ausführe und die eine große Liste von Daten von einer Website herunterlädt.

Für den Fall, dass der Endbenutzer zum Zeitpunkt der Verwendung eine sehr langsame oder fleckige Datenverbindung hat, möchte ich das AsyncTaskZeitlimit nach einer bestimmten Zeit festlegen. Mein erster Ansatz dazu ist wie folgt:

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

Nach dem Start von AsyncTaskwird ein neuer Handler gestartet, der den Vorgang AsyncTasknach 30 Sekunden abbricht, wenn er noch ausgeführt wird.

Ist das ein guter Ansatz? Oder ist etwas eingebaut AsyncTask, das für diesen Zweck besser geeignet ist?

Jake Wilson
quelle
18
Nachdem ich verschiedene Ansätze ausprobiert hatte, kam ich zu dem Schluss, dass Ihre Frage die akzeptierte Antwort sein sollte.
JohnEye
Vielen Dank für die Frage, dass ich wirklich in das gleiche Problem
geraten bin
1
Ihre Frage ist selbst eine gute Antwort +50 :)
karthik kolanji

Antworten:

44

Ja, es gibt AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Beachten Sie, dass durch Aufrufen im Hauptthread (AKA. UI-Thread) die Ausführung blockiert wird. Sie müssen ihn wahrscheinlich in einem separaten Thread aufrufen.

yorkw
quelle
9
Scheint, als würde es den Zweck der Verwendung von AsyncTask zunächst zunichte machen, wenn diese Timeout-Methode auf dem Haupt-UI-Thread ausgeführt wird ... Warum nicht einfach den handlerAnsatz verwenden, den ich in meiner Frage beschreibe? Scheint viel einfacher zu sein, da der UI-Thread nicht einfriert, während er darauf wartet, das Handler's Runnable auszuführen ...
Jake Wilson
Okay, danke, ich war mir einfach nicht sicher, ob es einen richtigen Weg gibt oder nicht.
Jake Wilson
3
Ich verstehe nicht, warum Sie get () in einem anderen Thread aufrufen. Es sieht komisch aus, weil AsyncTask selbst eine Art Thread ist. Ich denke, Jakobuds Lösung ist in Ordnung.
Smaragdhieu
Ich denke, .get () ähnelt dem Join des Posix-Threads, bei dem der Zweck darin besteht, auf den Abschluss des Threads zu warten. In Ihrem Fall dient es als Zeitüberschreitung, was eine umständliche Eigenschaft ist. Aus diesem Grund müssen Sie .get () vom Handler oder von einem anderen Thread aus aufrufen, um ein Blockieren der Hauptbenutzeroberfläche zu vermeiden
Constantine Samoilenko
2
Ich weiß, dass dies Jahre später ist, aber dies ist immer noch nicht in Android eingebaut, also habe ich eine Support-Klasse gemacht. Ich hoffe es hilft jemandem. gist.github.com/scottTomaszewski/…
Scott Tomaszewski
20

In dem Fall, dass Ihr Downloader auf einer URL-Verbindung basiert, verfügen Sie über eine Reihe von Parametern, mit denen Sie ein Timeout ohne komplexen Code definieren können:

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

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Wenn Sie diesen Code nur in Ihre asynchrone Aufgabe einbringen, ist dies in Ordnung.

'Read Timeout' dient zum Testen eines fehlerhaften Netzwerks während der gesamten Übertragung.

'Verbindungszeitlimit' wird nur zu Beginn aufgerufen, um zu testen, ob der Server aktiv ist oder nicht.

Clement Soullard
quelle
1
Ich bin einverstanden. Die Verwendung dieses Ansatzes ist einfacher und beendet die Verbindung tatsächlich, wenn das Zeitlimit erreicht ist. Bei Verwendung des AsyncTask.get () -Ansatzes werden Sie nur darüber informiert, dass das Zeitlimit erreicht wurde, der Download jedoch noch verarbeitet wird und möglicherweise zu einem späteren Zeitpunkt tatsächlich abgeschlossen wird, was zu weiteren Komplikationen im Code führt. Vielen Dank.
benommen
Bitte beachten Sie, dass dies nicht ausreicht, wenn Sie viele Threads bei sehr langsamen Internetverbindungen verwenden. Weitere Informationen: leakfromjavaheap.blogspot.nl/2013/12/… und thushw.blogspot.nl/2010/10/…
Kapitein Witbaard
20

Verwenden Sie die CountDownTimer-Klasse neben der erweiterten Klasse für AsyncTask in der onPreExecute () -Methode:

Hauptvorteil ist die Async-Überwachung, die intern in der Klasse durchgeführt wird.

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

Wenn Sie beispielsweise CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000) ändern, wird onTick () 6 Mal aufgerufen, bevor onFinish () aufgerufen wird. Dies ist gut, wenn Sie eine Überwachung hinzufügen möchten.

Danke für all die guten Ratschläge, die ich auf dieser Seite bekommen habe :-)

Gilco
quelle
2

Ich glaube nicht, dass so etwas in AsyncTask eingebaut ist. Ihr Ansatz scheint gut zu sein. Überprüfen Sie regelmäßig den Wert von isCancelled () in der doInBackground-Methode Ihrer AsyncTask, um diese Methode zu beenden, sobald der UI-Thread sie abbricht.

Wenn Sie die Verwendung des Handlers aus irgendeinem Grund vermeiden möchten, können Sie System.currentTimeMillis regelmäßig in Ihrer AsyncTask überprüfen und bei Zeitüberschreitung beenden, obwohl mir Ihre Lösung besser gefällt, da sie den Thread tatsächlich unterbrechen kann.

aleph_null
quelle
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
quelle
2
Erwägen Sie auch eine Erklärung
Saif Asif
0

Sie können eine weitere Bedingung festlegen, um die Stornierung robuster zu gestalten. z.B,

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

Inspirierend von der Frage habe ich eine Methode geschrieben, die eine Hintergrundaufgabe über AsyncTask ausführt. Wenn die Verarbeitung länger als LOADING_TIMEOUT dauert, wird ein Warndialog zum erneuten Versuch angezeigt.

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
quelle