Idealny sposób na anulowanie wykonującej AsyncTask

Uruchamiam zdalne operacje pobierania plików audio i odtwarzania plików audio w wątku tła za pomocą AsyncTask. Na czas uruchomienia operacji fetch wyświetlany jest pasek postępu Cancellable.

Chcę anulować / przerwać uruchamianie AsyncTask, gdy użytkownik anuluje (odrzuci) operację. Jaki jest idealny sposób załatwienia takiej sprawy?

Author: mghie, 2010-04-29

9 answers

Właśnie odkryłem, że AlertDialogs's boolean cancel(...); używam wszędzie właściwie nic nie robi. Świetnie.
Więc...

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

    // ...

}
 76
Author: yanchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-29 19:28:29

Jeśli robisz obliczenia :

  • musisz sprawdzać isCancelled() okresowo.

Jeśli wykonujesz żądanie HTTP :

  • Zapisz instancję swojego HttpGet lub HttpPost gdzieś (np. publiczne pole).
  • Po wywołaniu cancel, wywołanie request.abort(). To spowoduje, że IOException zostanie wrzucony do twojego doInBackground.

W moim przypadku miałem klasę złącza, której używałem w różnych asynchronicznych zadaniach. Aby było to proste, dodałem nową metodę abortAllRequests do tej klasy i wywołał tę metodę bezpośrednio po wywołaniu cancel.

 73
Author: wrygiel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-10 23:03:29

Rzecz w tym, że AsyncTask.cancel () wywołanie wywołuje tylko funkcję onCancel w twoim zadaniu. Tutaj chcesz obsłużyć żądanie anulowania.

Oto małe zadanie, którego używam do uruchomienia metody aktualizacji

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;
        }
     }
 19
Author: DonCroco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-07 10:48:20

Proste: nie używaj AsyncTask. AsyncTask jest przeznaczony do krótkich operacji, które kończą się szybko (dziesiątki sekund) i dlatego nie muszą być anulowane. "Odtwarzanie plików Audio" nie kwalifikuje się. Nie potrzebujesz nawet wątku w tle do zwykłego odtwarzania plików audio.

 11
Author: CommonsWare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-29 13:19:45

Jedynym sposobem na to jest sprawdzenie wartości metody iscanceled () i zatrzymanie odtwarzania po zwróceniu true.

 4
Author: dbyrne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-03 19:34:41

Tak piszę mój AsyncTask
kluczowym punktem jest dodaj wątek.sleep (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
Author: Andrew Chan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-02 09:10:25

Nasza globalna zmienna klasy AsyncTask

LongOperation LongOperationOdeme = new LongOperation();

I akcja KEYCODE_BACK, która przerywa AsyncTask

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            LongOperationOdeme.cancel(true);
        }
        return super.onKeyDown(keyCode, event);
    }
Dla mnie działa.
 0
Author: Göksel Güren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-11 10:21:16

Nie lubię zmuszać do niepotrzebnego przerywania moich zadań asynchronicznych cancel(true), ponieważ mogą one mieć zasoby do uwolnienia, takie jak zamykanie gniazd lub strumieni plików, Zapisywanie danych do lokalnej bazy danych itp. Z drugiej strony, miałem do czynienia z sytuacjami, w których zadanie asynchroniczne odmawia ukończenia się przez pewien czas, na przykład czasami, gdy główna czynność jest zamykana i żądam, aby zadanie asynchroniczne zakończyło się wewnątrz metody onPause() Działania. Więc to nie jest kwestia zwyczajnego wywołania running = false. Muszę wybrać rozwiązanie mieszane: oba wywołują running = false, następnie dają zadanie asynchroniczne kilka milisekund do zakończenia, a następnie wywołują cancel(false) lub cancel(true).

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

Jako wynik uboczny, po zakończeniu doInBackground(), czasami wywoływana jest metoda onCancelled(), a czasami onPostExecute(). Ale przynajmniej zakończenie zadania asynchronicznego jest gwarantowane.

 0
Author: Piovezan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-11 13:55:10

[[2]] w nawiązaniu do odpowiedzi Janczenki z 29 kwietnia '10: Użycie metody 'while (running)' jest dobre, gdy twój kod pod 'doInBackground' musi być wykonywany wiele razy podczas każdego wykonania Asynctasku. Jeśli twój kod pod 'doInBackground' musi być wykonany tylko raz na wykonanie Asynctasku, owinięcie całego kodu pod' doinbackground 'w pętli' while(running) ' nie zatrzyma kodu tła (wątku tła) przed uruchomieniem, gdy samo AsyncTask jest anulowane, ponieważ warunek 'while (running)' będzie oceniany tylko wtedy, gdy cały kod wewnątrz pętli while zostanie wykonany przynajmniej raz. Należy zatem albo (a.) Podziel swój kod pod 'doInBackground' na wiele bloków' while (running)' lub (b.) wykonaj liczne kontrole "iscanceled" w swoim kodzie "doInBackground", jak wyjaśniono w sekcji "anulowanie zadania" w https://developer.android.com/reference/android/os/AsyncTask.html .

Dla opcji (a.) można więc zmodyfikować Odpowiedź janczenki w następujący sposób:

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

// ...

Dla opcji (b.) Twój kod w 'doInBackground' będzie wyglądał mniej więcej tak:

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
Author: Alex Ivan Howard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-04 15:42:35