Android / AsyncTask: Вам все еще нужно проверить «isCancelled» (API 24)? - PullRequest
0 голосов
/ 04 июня 2018

Мое приложение использует AsyncTask для загрузки файлов при отображении ProgressDialog (я знаю, что оно устарело) с кнопкой "Отмена".

Согласно это вам следует периодически проверять isCancelled() в doInBackground, поскольку mytask.cancel(true) не будет прерывать doInBackground самостоятельно.

Сначала я просто отменил задачу, не проверив, и заметил, что она по-прежнему останавливается doInBackground: В зависимости от того, как долго я позволял загружать его перед нажатием кнопки «Отмена», я видел разные размеры в результирующем файле - от нескольких КБ до пары МБ - окончательный размер был бы около 9 МБ.

Как это возможно?Вам больше не нужно звонить isCancelled()?

Мой AsyncTask:

private class DownloadTask extends AsyncTask<String, String, String> {
    protected void onPreExecute() {
        progressdialog.setMessage("Preparing Download...");
        progressdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressdialog.setProgressNumberFormat(null);
        progressdialog.setProgressPercentFormat(null);
        progressdialog.setIndeterminate(true);
        progressdialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                progressdialog.dismiss();
                mytask.cancel(true);
            }
        });
        progressdialog.show();
    }

    protected String doInBackground(String... bla) {
        String error = download();
        return error;
    }

    protected void onProgressUpdate(String... s) {
        //....
    }

    protected void onPostExecute(String s) {
        progressdialog.dismiss();
        //....
    }

1 Ответ

0 голосов
/ 04 июня 2018

В соответствии с этим вы должны периодически проверять isCancelled () в doInBackground, потому что mytask.cancel (true) не будет прерывать doInBackground самостоятельно.

На самом деле это не так.

Согласно документации :

После вызова этого метода необходимо периодически проверять значение, возвращаемое isCancelled () из doInBackground (Object []), чтобы завершитьзадание как можно раньше.

Это означает, что вы можете дополнительно проверить, чтобы isCancelled() остановить AsyncTask раньше , если оно запущено.

mytask.cancel (true) все равно прекратит выполнение.

Давайте посмотрим, что происходит под капотом

Когда вы звоните mytask.cancel(true):

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

Где mFuture - это FutureTask, который поддерживает работоспособность внутри

Затем вызывается mFuture.cancel:

public boolean cancel(boolean mayInterruptIfRunning) {
    if (state != NEW)
        return false;
    if (mayInterruptIfRunning) {
        if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
            return false;
        Thread t = runner;
        if (t != null)
            t.interrupt();
        UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
    }
    else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
        return false;
    finishCompletion();
    return true;
}

Где runner это просто

private volatile Thread runner;

Поскольку это просто поток, давайте посмотрим, что interrupt делает в вашем случае:

Если этот поток заблокирован вОперация ввода / вывода для прерываемого канала, после чего канал будет закрыт, состояние прерывания потока будет установлено, и поток получит исключение ClosedByInterruptException.

Так что если ваш download() метод использует InterruptibleChannel interrupt будет работать.

Другими словами, похоже, что вам никогда не приходилось вызывать isCancelled() для прерывания AsyncTask =), поскольку Thread.interrupt может прекратить блокировку io в вашем случае.

...