一段时间后取消AsyncTask

时间:2013-06-26 08:41:21

标签: java android android-asynctask

这可能是一个重复的问题,但我找不到我想要的东西。 我在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()。

感谢。

5 个答案:

答案 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 - 操作系统根据需要自动终止并重新启动的组件。