Отменить AsyncTask через некоторое время
Это может быть дублированный вопрос, но я не нашел то, что искал. Я вызываю AsyncTask в пользовательском интерфейсе new LoadData().execute();
и в doInBackground я вызываю метод, который требует времени. Я хочу прервать этот поток, если данные не возвращаются через некоторое время. Ниже приведен код, как я пытался это сделать.
class LoadData extends AsyncTask<String, String, String>
{
@Override
protected void onPreExecute() {
super.onPreExecute();
startTime = System.currentTimeMillis();
}
protected String doInBackground(String... args)
{
DataCollector dc = new DataCollector();
data = dc.collectData(query);
//Here I check if the time is greater than 30 seconds then cancel
if(((System.currentTimeMillis()-startTime)/1000)>30)
{
cancel(true);
}
return null;
}
}
Но это не останавливает задачу через 30 секунд, на самом деле это занимает больше времени. Я попробовал get(long timeout, TimeUnit unit);
но это тоже не работает.
Может ли кто-нибудь показать мне, как я могу это сделать или как использовать isCancelled() в doInBackground.
Благодарю.
Ответы
Ответ 1
Вам понадобится поток, который отменяет вашу задачу через определенное время. Эта тема может выглядеть так:
public class TaskCanceler implements Runnable{
private AsyncTask task;
public TaskCanceler(AsyncTask task) {
this.task = task;
}
@Override
public void run() {
if (task.getStatus() == AsyncTask.Status.RUNNING )
task.cancel(true);
}
}
И когда вы вызываете свою AsyncTask, вам нужно запустить задачу cancle через определенное время (= таймаут, в этом случае 20 секунд)
private Handler handler = new Handler();
private TaskCanceler taskCanceler;
...
LoadData task = new LoadData();
taskCanceler = new TaskCanceler(task);
handler.postDelayed(taskCanceler, 20*1000);
task.execute(...)
Это хорошая идея, если вы очистите это отменить или закончить с помощью
if(taskCanceler != null && handler != null) {
handler.removeCallbacks(taskCanceler);
}
Вы можете, конечно, обернуть это в пользовательскую реализацию AsyncTask. Я использовал этот шаблон много раз, и он работает как шарм. Одно дело отметить, что в редких случаях обработчик не запускался, я подозреваю, что если вы создадите его в неправильном контексте, он не сохранится в определенных случаях, поэтому я заставил обработчик быть потоком пользовательского интерфейса с handler= new Handler(Looper.getMainLooper());
Ответ 2
Вы должны выполнить проверку времени в другом потоке.
В настоящее время вы выполняете: выполнение dc.collectData(query)
(в фоновом режиме), и как только оно будет готово, вы проверяете, следует ли отменять. Поэтому, если запрос занимает 1 минуту, вы выполните отмену проверки через 1 минуту, что уже слишком поздно.
Что вы можете сделать, так это запланировать TimerTask, который должен запускаться через 30 секунд после LoadData(). Execute(), и если задание таймера запущено, вы можете отменить AsyncTask (если он все еще запущен)
Ответ 3
Я бы перевел это на проблему async/await, сделав все дорогие методы как методы async.
Сначала измените DataCollector collectData (query) на collectDataAsync (запрос). (Если вы не можете изменить DataCollector, там есть работа, чтобы обернуть его в функцию лямбда или что-то подобное).
Во-вторых, измените doInBackground как задачу async, примерно так:
protected async Task<String> doInBackgroundAsync(String... args)
{
DataCollector dc = new DataCollector();
int timeout = 1000;
var task = dc.collectDataAsync(query);
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
data = task.Result;
} else {
// timeout logic
}
}
В принципе, у вас есть две задачи внутри doInBackgroundAsync: collectDataAsync и задача задержки. Ваш код ждет более быстрый. Тогда вы знаете, какой из них был, и вы можете реагировать соответственно.
Если вам также нужно отменить задачу collectDataAsync, вы хотите использовать cancelationToken. Я использую это, чтобы решить вашу проблему fooobar.com/questions/28721/....
Обратите внимание: теперь doInBackgroundAsync является асинхронным, поэтому он немного меняет способ его использования.
Надеюсь, поможет.
Ответ 4
попробуй это:
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
private final ProgressDialog progressDialog;
public MyTask(Context ctx) {
progressDialog = gimmeOne(ctx);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// actually could set running = false; right here, but I'll
// stick to contract.
cancel(true);
}
});
}
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected void onCancelled() {
running = false;
}
@Override
protected Void doInBackground(Void... params) {
while (running) {
// does the hard work
}
return null;
}
// ...
}
Предоставлено и для более подробной информации см. Этот ответ.
Ответ 5
Короткий ответ: вы НЕ МОЖЕТЕ отменить AsyncTask после его запуска. Что вы можете сделать, это вставить цикл внутри doInBackGround()
который будет проверять isCancelled()
и если в будущем он будет установлен как true, возвращайте значение из функции (которое, в свою очередь, вызовет onPostExecute()
если у вас есть определил его);
Обратите внимание, что только потому, что вы не можете остановить AsyncTask, это не означает, что ОС не отменит его, если он неактивен в памяти. Вы должны иметь это в виду, если вы выполняете важные задачи в AsyncTask (те, которые вы хотите выполнить на 100%). Если это так, лучше использовать службу - компонент, который автоматически будет убит и перезапущен ОС по мере необходимости.