webentwicklung-frage-antwort-db.com.de

Ideale Möglichkeit, eine ausgeführte AsyncTask abzubrechen

Ich führe Remote-Audiodatei-Abruf- und Audiodatei-Wiedergabevorgänge in einem Hintergrund-Thread aus, der AsyncTask verwendet. Eine Cancellable-Fortschrittsleiste wird angezeigt, wenn der Abrufvorgang ausgeführt wird. 

Ich möchte den Lauf AsyncTask abbrechen/abbrechen, wenn der Benutzer die Operation abbricht (sich dagegen entscheidet). Was ist der ideale Weg, um mit einem solchen Fall umzugehen?

107
Samuh

Ich habe gerade entdeckt, dass AlertDialogss boolean cancel(...);, die ich überall verwendet habe, eigentlich nichts tut. Großartig.
So... 

public class MyTask extends AsyncTask<Void, Void, Void> {

    private volatile boolean running = true;
    private final ProgressDialog progressDialog;

    public MyTask(Context ctx) {
        progressDialog = gimmeOne(ctx);

        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                // actually could set running = false; right here, but I'll
                // stick to contract.
                cancel(true);
            }
        });

    }

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected void onCancelled() {
        running = false;
    }

    @Override
    protected Void doInBackground(Void... params) {

        while (running) {
            // does the hard work
        }
        return null;
    }

    // ...

}
78
yanchenko

Wenn Sie Berechnungen durchführen :

  • Sie müssen isCancelled() regelmäßig überprüfen.

Wenn Sie eine HTTP-Anfrage machen :

  • Speichern Sie die Instanz Ihrer HttpGet oder HttpPost irgendwo (zB ein öffentliches Feld).
  • Nach dem Aufruf von cancel rufen Sie request.abort() auf. Dadurch wird IOException in Ihre doInBackground geworfen.

In meinem Fall hatte ich eine Verbindungsklasse, die ich in verschiedenen AsyncTasks verwendete. Um es einfach zu halten, habe ich dieser Klasse eine neue abortAllRequests-Methode hinzugefügt und diese Methode direkt nach dem Aufruf von cancel aufgerufen.

75
wrygiel

Die Sache ist, dass der Aufruf von AsyncTask.cancel () nur die Funktion onCancel in Ihrer Task aufruft. Hier möchten Sie die Abbruchanforderung bearbeiten.

Hier ist eine kleine Aufgabe, die ich verwende, um eine Aktualisierungsmethode auszulösen

private class UpdateTask extends AsyncTask<Void, Void, Void> {

        private boolean running = true;

        @Override
        protected void onCancelled() {
            running = false;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            onUpdate();
        }

        @Override
        protected Void doInBackground(Void... params) {
             while(running) {
                 publishProgress();
             }
             return null;
        }
     }
20
DonCroco

Einfach: Verwenden Sie keine AsyncTask. AsyncTask ist für kurze Operationen konzipiert, die schnell enden (mehrere zehn Sekunden) und daher nicht abgebrochen werden müssen. "Audiodatei-Wiedergabe" ist nicht qualifiziert. Sie benötigen nicht einmal einen Hintergrund-Thread für die Wiedergabe normaler Audiodateien.

11
CommonsWare

So schreibe ich meine AsyncTask
Der Schlüsselpunkt ist add.sigep (1). 

@Override   protected Integer doInBackground(String... params) {

        Log.d(TAG, PRE + "url:" + params[0]);
        Log.d(TAG, PRE + "file name:" + params[1]);
        downloadPath = params[1];

        int returnCode = SUCCESS;
        FileOutputStream fos = null;
        try {
            URL url = new URL(params[0]);
            File file = new File(params[1]);
            fos = new FileOutputStream(file);

            long startTime = System.currentTimeMillis();
            URLConnection ucon = url.openConnection();
            InputStream is = ucon.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);

            byte[] data = new byte[10240]; 
            int nFinishSize = 0;
            while( bis.read(data, 0, 10240) != -1){
                fos.write(data, 0, 10240);
                nFinishSize += 10240;
                **Thread.sleep( 1 ); // this make cancel method work**
                this.publishProgress(nFinishSize);
            }              
            data = null;    
            Log.d(TAG, "download ready in"
                  + ((System.currentTimeMillis() - startTime) / 1000)
                  + " sec");

        } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                returnCode = FAIL;
        } catch (Exception e){
                 e.printStackTrace();           
        } finally{
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                e.printStackTrace();
            }
        }

        return returnCode;
    }
4
Andrew Chan

Sie können dies nur tun, indem Sie den Wert der isCancelled () -Methode überprüfen und die Wiedergabe anhalten, wenn true zurückgegeben wird.

4
dbyrne

Unsere globale AsyncTask-Klassenvariable 

LongOperation LongOperationOdeme = new LongOperation();

Und KEYCODE_BACK-Aktion, die AsyncTask unterbricht

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            LongOperationOdeme.cancel(true);
        }
        return super.onKeyDown(keyCode, event);
    }

Für mich geht das.

0
Göksel Güren

In Bezug auf die Antwort von Yanchenko vom 29. April '10: Die Verwendung von 'while (running)' ist ordentlich, wenn Ihr Code unter 'doInBackground' bei jeder Ausführung der AsyncTask mehrmals ausgeführt werden muss. Wenn Ihr Code unter 'doInBackground' nur einmal pro Ausführung der AsyncTask ausgeführt werden muss, kann der gesamte Code unter 'doInBackground' in einer 'while (running)' - Schleife ausgeführt werden, wenn der Hintergrundcode (Hintergrundthread) nicht ausgeführt wird AsyncTask selbst wird abgebrochen, da die Bedingung 'while (running)' nur ausgewertet wird, wenn der gesamte Code in der while-Schleife mindestens einmal ausgeführt wurde. Sie sollten daher entweder (A.) Ihren Code unter 'doInBackground' in mehrere 'while (running)' Blöcke aufteilen oder (_) zahlreiche 'isCancelled'-Prüfungen in Ihrem' doInBackground'-Code durchführen, wie Erklärt unter "Abbrechen einer Aufgabe" unter https://developer.Android.com/reference/Android/os/AsyncTask.html .

Für die Option (a.) Kann man also die Antwort von Yanchenko wie folgt ändern:

public class MyTask extends AsyncTask<Void, Void, Void> {

private volatile boolean running = true;

//...

@Override
protected void onCancelled() {
    running = false;
}

@Override
protected Void doInBackground(Void... params) {

    // does the hard work

    while (running) {
        // part 1 of the hard work
    }

    while (running) {
        // part 2 of the hard work
    }

    // ...

    while (running) {
        // part x of the hard work
    }
    return null;
}

// ...

Bei Option (b.) Sieht Ihr Code in 'doInBackground' etwa so aus:

public class MyTask extends AsyncTask<Void, Void, Void> {

//...

@Override
protected Void doInBackground(Void... params) {

    // part 1 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // part 2 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // ...

    // part x of the hard work
    // ...
    if (isCancelled()) {return null;}
}

// ...
0

Ich mag es nicht, meine asynchronen Aufgaben unnötig mit cancel(true) zu unterbrechen, da sie möglicherweise Ressourcen haben müssen, die freigegeben werden müssen, z. B. das Schließen von Sockets oder Dateistreams, das Schreiben von Daten in die lokale Datenbank usw. Auf der anderen Seite bin ich mit Situationen konfrontiert Die asynchrone Task weigert sich, sich zeitweise zu beenden, zum Beispiel manchmal, wenn die Hauptaktivität geschlossen wird, und ich fordere die async-Task auf, die onPause()-Methode der Aktivität zu beenden. Es geht also nicht darum, einfach running = false aufzurufen. Ich muss für eine gemischte Lösung gehen: Beide rufen running = false an, geben dann der asynchronen Task einige Millisekunden zum Abschluss und rufen dann entweder cancel(false) oder cancel(true) auf.

if (backgroundTask != null) {
    backgroundTask.requestTermination();
    try {
        Thread.sleep((int)(0.5 * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (backgroundTask.getStatus() != AsyncTask.Status.FINISHED) {
        backgroundTask.cancel(false);
    }
    backgroundTask = null;
}

Als Nebenergebnis wird nach doInBackground() manchmal die onCancelled()-Methode und manchmal onPostExecute() aufgerufen. Aber zumindest die Beendigung der asynchronen Aufgabe ist garantiert.

0
Piovezan