Android: Отмена Асинхронной Задачи
Я использую асинхронные задачи, чтобы загрузить изображение и получить некоторые результаты.
при загрузке изображения я вижу диалоговое окно прогресса, написанное в методе onPreExecute () следующим образом:
protected void onPreExecute() {
uploadingDialog = new ProgressDialog(MyActivity.this);
uploadingDialog.setMessage("uploading");
uploadingDialog.setCancelable(true);
uploadingDialog.show();
}
Ok когда я нажимаю кнопку назад, очевидно, диалог исчезает из-за setCancelable(true).
но (очевидно) асинхронная задача не остановить.
Так как я могу это исправить? Я хочу отменить как диалог, так и асинхронную задачу при нажатии кнопки "Назад". Любой идеи?
EDIT: НАШЕЛ РЕШЕНИЕ. СМОТРИТЕ МОЙ ОТВЕТ НИЖЕ.
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
theonCancelled()
(метод обратного вызова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.