Идеальный способ отменить выполнение AsyncTask

я запускаю удаленные операции извлечения аудиофайлов и воспроизведения аудиофайлов в фоновом потоке с помощью AsyncTask. А Cancellable индикатор выполнения отображается во время выполнения операции выборки.

Я хочу отменить / отменить AsyncTask запуск, когда пользователь отменяет (решает против) операцию. Каков идеальный способ справиться с таким случаем?

9 ответов


обнаружила, что AlertDialogs ' s boolean cancel(...); Я использую везде на самом деле ничего не делает. Отличный.
Так...

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

    // ...

}

если вы делаете вычисления:

  • вы должны проверить isCancelled() периодически.

если вы делаете HTTP-запрос:

  • сохранить экземпляр HttpGet или HttpPost где-то (например. публичное поле.)
  • после вызова cancel, называют request.abort(). Это вызовет IOException быть брошенным внутри doInBackground.

в моем случае у меня был класс разъема, который я используется в различных AsyncTasks. Чтобы все было просто, я добавил новый abortAllRequests метод для этого класса и называется этот метод непосредственно после вызова cancel.


дело в том, что AsyncTask.вызов cancel () вызывает только функцию onCancel в вашей задаче. Здесь вы хотите обработать запрос на отмену.

вот небольшая задача, которую я использую для запуска метода обновления

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

простой: не используйте AsyncTask. AsyncTask предназначен для коротких операций, которые заканчиваются быстро (десятки секунд) и поэтому не должны быть отменены. "Воспроизведение аудиофайлов" не подходит. Вам даже не нужен фоновый поток для обычного воспроизведения аудиофайлов.


единственный способ сделать это-проверить значение метода isCancelled() и остановить воспроизведение, когда он возвращает true.


вот как я пишу свою AsyncTask
главное-добавить нить.сна(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;
    }

наша глобальная переменная класса AsyncTask

LongOperation LongOperationOdeme = new LongOperation();

и действие KEYCODE_BACK, которое прерывает AsyncTask

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

это работает для меня.


мне не нравится заставлять прерывать мои асинхронные задачи с cancel(true) излишне, потому что у них могут быть освобожденные ресурсы, такие как закрытие сокетов или файловых потоков, запись данных в локальную базу данных и т. д. С другой стороны, я сталкивался с ситуациями, когда асинхронная задача отказывается заканчивать себя часть времени, например, иногда, когда основное действие закрывается, и я прошу асинхронную задачу закончить изнутри onPause() метод. Так что это не просто зову running = false. Я должен пойти на смешанное решение: оба вызова running = false, затем дать асинхронной задаче несколько миллисекунд, чтобы закончить, а затем вызвать либо cancel(false) или 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;
}

в качестве побочного результата, после doInBackground() заканчивается, иногда onCancelled() вызывается метод, а иногда onPostExecute(). Но, по крайней мере, завершение асинхронной задачи гарантировано.


со ссылкой на ответ Янченко от 29 апреля ' 10: Использование подхода "while (running)" является аккуратным, когда ваш код под "doInBackground" должен выполняться несколько раз во время каждого выполнения AsyncTask. Если ваш код под "doInBackground" должен выполняться только один раз за выполнение AsyncTask, упаковка всего вашего кода под "doInBackground" в цикле "while(running)" не остановит фоновый код (фоновый поток) от запуска, когда сама AsyncTask отменена, потому что условие "while (running)" будет оцениваться только один раз, когда весь код внутри цикла while был выполнен хотя бы один раз. Вы должны таким образом либо а) разбейте код в разделе "doInBackground" на несколько блоков "while(running)" или b) выполните многочисленные проверки "isCancelled" по всему коду "doInBackground", как описано в разделе" отмена задачи " в https://developer.android.com/reference/android/os/AsyncTask.html.

для опции (a.) можно таким образом, измените ответ Янченко следующим образом:

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

// ...

для варианта (b.) ваш код в "doInBackground" будет выглядеть примерно так:

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

// ...