Android: Отмена Асинхронной Задачи


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

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

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

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

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

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

EDIT: НАШЕЛ РЕШЕНИЕ. СМОТРИТЕ МОЙ ОТВЕТ НИЖЕ.

8 52

8 ответов:

из SDK:

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

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

После вызова этого метода, 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
}

НАШЕЛ РЕШЕНИЕ: Я добавил прослушиватель действий перед загрузкой dialog.показать () вот так:

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

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

    MyAsyncTask myTask=null;

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

    myTask = new MyAsyncTask();
    myTask.execute();

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

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

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

YourAsyncTask mTask;
Dialog mDialog;

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

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

if(mTask!=null) mTask.cancel(); 
if(mDialog!=null) mDialog.dismiss();

Я хотел бы улучшить код. Когда вы можетеaSyncTask the 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();
        }

    }

большую часть времени, когда я использую 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;
    }
}

вы можете просто попросить об отмене, но на самом деле не прекратить его. Смотрите это ответ.

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

volatile boolean running;

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

    running=true;
    ...
    }


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

    running=false;
    ...
}

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