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