Android - Отменить AsyncTask Принудительно


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

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

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

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

обновление:

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

6 57

6 ответов:

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

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

вызов cancel() на AsyncTask. Будет ли это на самом деле отменить что-нибудь зависит немного от того, что вы делаете. Процитирую Ромена Гая:

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

Это действительно зависит от того, что вы делаете в вашем asynctask.

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

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

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

упомянутый в комментариях случай что 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() не должно, " Android API doc говорит:вызов метода 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();
}

несмотря на то, что 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();
    }
}

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

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