Android: отменить асинхронную задачу - PullRequest
56 голосов
/ 18 мая 2011

Я использую асинхронную задачу для загрузки изображения и получения некоторых результатов.

При загрузке изображения я вижу диалог прогресса, написанный в методе onPreExecute (), например:

    protected void onPreExecute() { 
         uploadingDialog = new ProgressDialog(MyActivity.this); 
         uploadingDialog.setMessage("uploading"); 
         uploadingDialog.setCancelable(true);
         uploadingDialog.show();
    }

Хорошо, когда я нажимаю кнопку «назад», очевидно, что диалог исчезает из-за setCancelable (true).

Но (очевидно) асинхронная задача не останавливается.

Так как я могу это исправить? Я хочу отменить как диалоговое, так и асинхронное задание, когда нажимаю кнопку возврата. Есть идеи?

РЕДАКТИРОВАТЬ: НАЙТИ РЕШЕНИЕ . СМОТРИТЕ МОЙ ОТВЕТ НИЖЕ.

Ответы [ 9 ]

100 голосов
/ 19 мая 2011

Из SDK:

Отмена задания

Задание можно отменить в любое время, вызвав отмену (булево). Вызов этого метода вызовет последующие вызовы isCancelled () вернуть истину.

После вызова этого метода вместо OnCancelled (Object) вместо onPostExecute (Object) будет вызвано после возврата doInBackground (Object []).

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

Итак, ваш код подходит для прослушивателя диалогов:

uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    public void onCancel(DialogInterface dialog) {
        myTask.cancel(true);
        //finish();
    }
});

Теперь, как я упоминал ранее из SDK, вы должны проверить, отменена ли задача или нет, для этого вы должны проверить isCancelled () внутри метода onPreExecute ().

Например:

if (isCancelled()) 
    break;
else
{
   // do your work here
}
32 голосов
/ 12 октября 2011

НАЙДЕНО РЕШЕНИЕ : я добавил слушатель действия перед загрузкойDialog.show () следующим образом:

    uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
          public void onCancel(DialogInterface dialog) {
              myTask.cancel(true);
              //finish();
          }
    });

Таким образом, когда я нажимаю кнопку возврата, вышеуказанный OnCancelListener отменяет обадиалог и задача.Также вы можете добавить finish (), если хотите завершить все действия на нажатой спине.Не забудьте объявить асинхронную задачу как переменную, подобную этой:

    MyAsyncTask myTask=null;

, и выполнить асинхронную задачу следующим образом:

    myTask = new MyAsyncTask();
    myTask.execute();
10 голосов
/ 04 августа 2011

Я потратил некоторое время на выяснение этого, все, что я хотел, это простой пример того, как это сделать, поэтому я решил опубликовать, как я это сделал.Это некоторый код, который обновляет библиотеку и имеет диалоговое окно прогресса, показывающее, сколько книг было обновлено, и отменяет его, когда пользователь закрывает диалоговое окно:

private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
    private ProgressDialog dialog = new ProgressDialog(Library.this);
    private int total = Library.instance.appState.getAvailableText().length;
    private int count = 0;

    //Used as handler to cancel task if back button is pressed
    private AsyncTask<Void, Integer, Boolean> updateTask = null;

    @Override
    protected void onPreExecute(){
        updateTask = this;
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setOnDismissListener(new OnDismissListener() {               
            @Override
            public void onDismiss(DialogInterface dialog) {
                updateTask.cancel(true);
            }
        });
        dialog.setMessage("Updating Library...");
        dialog.setMax(total);
        dialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... arg0) {
            for (int i = 0; i < appState.getAvailableText().length;i++){
                if(isCancelled()){
                    break;
                }
                //Do your updating stuff here
            }
        }

    @Override
    protected void onProgressUpdate(Integer... progress){
        count += progress[0];
        dialog.setProgress(count);
    }

    @Override
    protected void onPostExecute(Boolean finished){
        dialog.dismiss();
        if (finished)
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
        else 
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
    }
}
9 голосов
/ 18 мая 2011

создайте некоторые переменные-члены в вашей деятельности, например

YourAsyncTask mTask;
Dialog mDialog;

, используйте их для диалогов и задач;

в onPause () просто вызовите

if(mTask!=null) mTask.cancel(); 
if(mDialog!=null) mDialog.dismiss();
5 голосов
/ 15 мая 2015

Я хотел бы улучшить код. Когда вы отключаете aSyncTask, автоматически вызывается onCancelled() (метод обратного вызова aSyncTask), и там вы можете скрыть свой progressBarDialog.

Вы также можете включить этот код:

public class information extends AsyncTask<String, String, String>
    {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... arg0) {
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            this.cancel(true);
        }

        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onCancelled() {
            Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
            dialog.hide(); /*hide the progressbar dialog here...*/
            super.onCancelled();
        }

    }
3 голосов
/ 30 октября 2012

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

В итоге я передаю свою задачу бизнес-объекту, чтобы он мог обрабатывать отмену. Моя установка такова:

public abstract class MyActivity extends Activity {

    private Task mTask;
    private Business mBusiness;

    public void startTask() {
        if (mTask != null) {
            mTask.cancel(true);
        }
        mTask = new mTask();
        mTask.execute();
    }
}

protected class Task extends AsyncTask<Void, Void, Boolean> {
    @Override
    protected void onCancelled() {
        super.onCancelled();

        mTask.cancel(true);

        // ask if user wants to try again
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        return mBusiness.synchronize(this);
    }

    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);

        mTask = null;

        if (result) {
            // done!
        }
        else {
            // ask if user wants to try again
        }
    }
}

public class Business {
    public boolean synchronize(AsyncTask<?, ?, ?> task) {
        boolean response = false;
        response = loadStuff(task);

        if (response)
            response = loadMoreStuff(task);

        return response;
    }

    private boolean loadStuff(AsyncTask<?, ?, ?> task) {
        if (task != null && task.isCancelled()) return false;

        // load stuff

        return true;
    }
}
2 голосов
/ 26 мая 2013

У меня была похожая проблема - по сути, я получал NPE в асинхронной задаче после того, как пользователь уничтожил активность.Изучив проблему переполнения стека, я выбрал следующее решение:

volatile boolean running;

public void onActivityCreated (Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    running=true;
    ...
    }


public void onDestroy() {
    super.onDestroy();

    running=false;
    ...
}

Затем я периодически проверяю «работает» в своем асинхронном коде.Я прошел стресс-тестирование, и теперь я не могу «сломать» свою деятельность.Это прекрасно работает и имеет то преимущество, что оно проще, чем некоторые решения, которые я видел в SO.

2 голосов
/ 18 мая 2011

Вы можете просто запросить отмену, но не прекратить ее действие.Смотрите этот ответ .

1 голос
/ 05 апреля 2019

Как отменить AsyncTask

Полный ответ здесь - Пример Android AsyncTask

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

отмена (логическое значение mayInterruptIfitRunning)

myTask.cancel (false) - возвращает isCancelled значение true. Помогает отменить задание.

myTask.cancel (true) - isCancelled () также возвращает true, прерывает фоновый поток и освобождает ресурсы.

Это считается высокомерным способом. Если есть какой-либо метод thread.sleep (), выполняющий в фоновом потоке, метод cancel (true) прервет фоновый поток в это время. Но отмена (false) будет ждать его и отменит задачу, когда этот метод завершится.

Если вы вызываете cancel (), а doInBackground () еще не началось, выполните. onCancelled () вызовет.

После вызова отмены (…) вы должны периодически проверять значение, возвращаемое isCancelled () в doInbackground (). как показано ниже.

protected Object doInBackground(Params… params)  { 
 while (condition)
{
 ...
if (isCancelled()) 
break;
}
return null; 
}
...