Идеальный способ отменить выполнение AsyncTask - PullRequest
107 голосов
/ 29 апреля 2010

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

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

Ответы [ 9 ]

78 голосов
/ 29 апреля 2010

Только что обнаружил, что AlertDialogs 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;
    }

    // ...

}
76 голосов
/ 11 июня 2012

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

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

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

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

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

20 голосов
/ 05 июля 2011

Дело в том, что вызов 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;
        }
     }
11 голосов
/ 29 апреля 2010

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

4 голосов
/ 02 июня 2011

Вот как я пишу свой AsyncTask
ключевой момент - добавить Thread.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 голосов
/ 03 мая 2010

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

0 голосов
/ 04 января 2018

Со ссылкой на ответ Янченко от 29 апреля 2010 года: Подход 'while (running)' изящен, когда ваш код в doInBackground должен выполняться несколько раз во время каждого выполнения AsyncTask. Если ваш код в «doInBackground» должен выполняться только один раз за выполнение AsyncTask, завершение всего вашего кода в «doInBackground» в цикле «while (выполняется)» не остановит выполнение фонового кода (фонового потока), когда Сам AsyncTask отменяется, потому что условие while (выполняется) будет оцениваться только после того, как весь код цикла while будет выполнен хотя бы один раз. Таким образом, вы должны либо (а.) разбить ваш код в doInBackground на несколько блоков «в то время как (работает)» или (б) выполнить многочисленные проверки isCancelled в коде doInBackground, как описано в разделе «Отмена задачи» на https://developer.android.com/reference/android/os/AsyncTask.html.

Для варианта (а) можно изменить ответ Янченко следующим образом:

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

// ...
0 голосов
/ 11 июня 2013

Мне не нравится принудительно прерывать мои асинхронные задачи с помощью 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(). Но по крайней мере завершение асинхронной задачи гарантировано.

0 голосов
/ 11 февраля 2013

Наша глобальная переменная класса 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);
    }

У меня это работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...