Android - Отменить AsyncTask Принудительно
я реализовал AsyncTask в моем одном из действий:
performBackgroundTask asyncTask = new performBackgroundTask();
asyncTask.execute();
Теперь мне нужно реализовать функцию кнопки "Отмена", поэтому я должен остановить выполнение запущенной задачи. Я не знаю, как остановить выполнение задачи(фоновая задача).
поэтому, пожалуйста, предложите мне, как я могу отменить AsyncTask принудительно ?
обновление:
Я нашел о Cancel()
метод тот же, но я нашел, что вызов cancel(boolean mayInterruptIfRunning)
не обязательно останавливать выполнение фонового процесса. Все, что, кажется, происходит, это то, что AsyncTask выполнит onCancelled () и не будет запускать onPostExecute (), когда он завершится.
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); }
это работает для меня.