Android - принудительно отмените AsyncTask - PullRequest
62 голосов
/ 20 января 2011

Я реализовал AsyncTask в одном из своих действий:

 performBackgroundTask asyncTask = new performBackgroundTask();
 asyncTask.execute();

Теперь мне нужно реализовать функциональность кнопки «Отмена», поэтому мне нужно остановить выполнение запущенной задачи.Я не знаю, как остановить текущее задание (фоновое задание).

Поэтому, пожалуйста, предложите мне, как я могу принудительно отменить AsyncTask?

Обновление:

Я нашел о том же методе Cancel(), но обнаружил, что вызов cancel(boolean mayInterruptIfRunning) не обязательно останавливает выполнение фонапроцесс.Все, что, кажется, происходит, - то, что AsyncTask будет выполнять onCancelled () и не будет запускаться onPostExecute () после его завершения.

Ответы [ 6 ]

90 голосов
/ 23 января 2011

Просто отметьте isCancelled() время от времени:

 protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled()) break;
    }
    return null;
 }
49 голосов
/ 20 января 2011

Звоните cancel() на AsyncTask.Будет ли это на самом деле отменять что-либо, немного зависит от того, что вы делаете.Чтобы процитировать Romain Guy:

Если вы вызываете метод отмены (true), прерывание будет отправлено в фоновый поток, что может помочь прерываемым задачам.В противном случае вы должны просто регулярно проверять isCancelled () в вашем методе doInBackground ().Вы можете увидеть примеры этого на code.google.com/p/shelves.

16 голосов
/ 20 января 2011

Это действительно зависит от того, что вы делаете в своей асинхронной задаче.

Если это цикл, обрабатывающий много файлов, вы можете просто после каждого файла проверять, установлен флаг isCanceled () или нет, а затем прерывать цикл, если он есть.

Если это однострочная команда, выполняющая очень длинную операцию, вы мало что можете сделать.

Лучшим обходным решением будет не использовать метод отмены асинхронной задачи и использовать свой логический параметр cancelFlag. Затем вы можете протестировать этот cancelFlag в вашем postExecute, чтобы решить, что делать с результатом.

5 голосов
/ 03 ноября 2013

В комментариях упоминается, что isCancelled() always returns false even i call asynctask.cancel(true); особенно вредно, если я закрываю свое приложение, но AsyncTask продолжает работать.

Для решения этой проблемы я изменил предложенный код Jacob Nordfalk следующим образом:

protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled() || (FlagCancelled == true)) break;
    }
    return null;
 }

и добавил следующее к основному виду деятельности:

@Override
protected void onStop() {
    FlagCancelled = true;
    super.onStop();
}

Поскольку мой AsyncTask был закрытым классом одного из представлений, поэтому получатели или установщики флага были обязаны сообщитьAsyncTask о текущем значении флага.

Мои множественные тесты (AVD Android 4.2.2, Api 17) показали, что если AsyncTask уже выполняет свой doInBackground, то isCancelled() никак не реагирует(т. е. продолжает быть ложным) при любых попытках его отмены, например, во время mViewGroup.removeAllViews(); или во время OnDestroy из MainActivity, каждое из которых приводит к отключению представлений

   @Override 
   protected  void  onDetachedFromWindow() { 
    mAsyncTask.cancel(false); // and the same result with mAsyncTask.cancel(true);
    super.onDetachedFromWindow(); 
   } 

Еслиудалось принудительно остановить doInBackground() благодаря введенному FlagCancelled, затем вызывается onPostExecute(), но ни onCancelled(), ни onCancelled(Void result) (начиная с уровня API 11) не вызываются.(Я понятия не имею, почему, потому что они должны вызываться, а onPostExecute() не должен ", - говорит док API API: вызов метода cancel () гарантирует, что onPostExecute (Object) никогда не будет вызываться." - IdleSun, отвечая на аналогичный вопрос ).

С другой стороны, если тот же AsyncTask не начал свою doInBackground() до отмены, то все в порядке, isCancelled() меняется на true, и я могу проверитьчто в

@Override
    protected void onCancelled() {
        Log.d(TAG, String.format("mAsyncTask - onCancelled: isCancelled = %b, FlagCancelled = %b", this.isCancelled(), FlagCancelled ));
    super.onCancelled();
}
2 голосов
/ 27 ноября 2013

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

В этом мы сталкиваемся с трудностями. 1. Обычный диалог прогресса, отображаемый с помощью AsyncTask, - это первое, что отменяется в AsyncTask, когда пользователь нажимает кнопку возврата. 2. AsyncTask может быть в методе doInBackground

Создав dismissDialogListerner на ProgressDialog, пользователь может нажать кнопку «Назад» и фактически аннулировать AsycnTask и закрыть само диалоговое окно.

Вот пример:

public void openMainLobbyDoor(String username, String password){
    if(mOpenDoorAsyncTask == null){
        mOpenDoorAsyncTask = (OpenMainDoor) new OpenMainDoor(username, password, Posts.API_URL, 
                mContext, "Please wait while I unlock the front door for you!").execute(null, null, null);
    }
}

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

    //declare needed variables
    String username, password, url, loadingMessage;
    int userValidated;
    boolean canConfigure;
    Context context;
    ProgressDialog progressDialog;

    public OpenMainDoor(String username, String password, String url, 
                Context context, String loadingMessage){
        userValidated = 0;
        this.username = username;
        this.password = password;
        this.url = url;
        this.context = context;
        this.loadingMessage = loadingMessage;
    }

    /**
     * used to cancel dialog on configuration changes
     * @param canConfigure
     */
    public void canConfigureDialog(boolean canConfigure){
        this.canConfigure = canConfigure;
    }

    @Override
    protected void onPreExecute(){
        progressDialog = new ProgressDialog(this.context);
        progressDialog.setMessage(loadingMessage);
        progressDialog.setIndeterminate(true);
        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                mOpenDoorAsyncTask.cancel(true);
            }
        });
        progressDialog.show();
        this.canConfigure = true;
    }

    @Override
    protected Void doInBackground(Void... params) {
        userValidated = Posts.authenticateNTLMUserLogin(username, password, url, context);
        while(userValidated == 0){
            if(isCancelled()){
                break;
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void unused){
        //determine if this is still attached to window
        if(canConfigure)
            progressDialog.dismiss();

        if(userValidated == 1){
            saveLoginValues(username, password, true);
            Toast.makeText(context, R.string.main_login_pass, Toast.LENGTH_SHORT).show();
        }else{
            saveLoginValues(username, password, false);
            Toast.makeText(context, R.string.main_login_fail, Toast.LENGTH_SHORT).show();
        }
        nullifyAsyncTask();
    }

    @Override
    protected void onCancelled(){
        Toast.makeText(context, "Open door request cancelled!", Toast.LENGTH_SHORT).show();
        nullifyAsyncTask();
    }
}
1 голос
/ 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, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...