这可能是一个重复的问题,但我找不到我想要的东西。
我在UI活动new LoadData().execute();
中调用AsyncTask,在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);
,但这也不起作用。
任何人都可以告诉我该怎么做或者如何在doInBackground中使用isCancelled()。
感谢。
答案 0 :(得分:11)
您需要一个在一定时间后取消任务的线程。那个线程看起来像这样:
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());
<的UI线程/ p>
答案 1 :(得分:3)
你必须在另一个线程上进行时间检查。
您目前所做的是:执行dc.collectData(query)
(在后台),一旦准备就绪,您可以检查是否应该取消。因此,如果查询需要1分钟,您将在1分钟后进行取消检查,这已经太晚了。
你可以做的是安排一个TimerTask,它应该在LoadData()。execute()之后运行30秒,如果运行了timer Task,你可以取消AsyncTask(如果它还在运行)
答案 2 :(得分:2)
我会将此转换为异步/等待问题,使所有昂贵的方法成为异步方法。
首先,将DataDataCollector的collectData(查询)修改为collectDataAsync(查询)。 (如果你不能修改DataCollector,有办法将它包装在lambda函数或类似的东西中。)
其次,将doInBackground更改为异步任务,如下所示:
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任务,那么您想要使用cancellationToken。 我用它来解决你的问题https://stackoverflow.com/a/11191070/3307066。
请注意,现在doInBackgroundAsync是一个异步,所以它改变了使用它的方式。
希望它有所帮助。
答案 3 :(得分:0)
试试这个:
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;
}
// ...
}
Courtesy有关详细信息,请参阅此答案。
答案 4 :(得分:0)
简短的回答是,一旦启动,您就无法取消AsyncTask。您可以做的是在doInBackGround()
内部插入一个循环,它将检查isCancelled()
,如果在将来的某个时间将其设置为true,则返回该函数的值(该函数将依次调用{{ 1}}如果你定义了它);
请注意,仅仅因为您无法停止AsyncTask并不意味着如果内存不足,操作系统将不会取消它。如果您正在执行AsyncTask中的基本任务(您希望100%执行的任务),请记住这一点。如果是这样,最好使用Service - 操作系统根据需要自动终止并重新启动的组件。