15

这可能是一个重复的问题,但我没有找到我要找的东西。我在 UI 活动中调用 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);,但这也不起作用。

谁能告诉我我该怎么做或如何在 doInBackground 中使用 isCancelled()。

谢谢。

4

5 回答 5

12

您需要一个线程在一定时间后取消您的任务。该线程可能如下所示:

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 时,您需要在一定时间后运行取消任务(=超时,在本例中为 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 的自定义实现中。我已经多次使用这种模式,它就像一个魅力。需要注意的一点是,在极少数情况下处理程序不会启动,我怀疑如果您在错误的上下文中创建它,它在某些情况下将无法生存,所以我强制处理程序成为 UI 线程handler= new Handler(Looper.getMainLooper());

于 2013-07-01T12:21:07.193 回答
3

您必须在不同的线程上进行时间检查。

您当前所做的是:执行dc.collectData(query)(在后台),一旦准备就绪,您检查是否应该取消。因此,如果查询需要 1 分钟,您将在 1 分钟后进行取消检查,这已经为时已晚。

你可以做的是安排一个 TimerTask 应该在 LoadData().execute() 之后运行 30 秒,如果计时器任务正在运行,你可以取消 AsyncTask(如果它仍在运行)

于 2013-06-26T08:44:22.513 回答
2

我会将其转换为异步/等待问题,使所有昂贵的方法都成为异步方法。

首先,将DataCollector的collectData(query)修改为collectDataAsync(query)。(如果您无法修改 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 任务,那么您需要使用 cancelToken。我用它来解决您的问题https://stackoverflow.com/a/11191070/3307066

请注意,现在 doInBackgroundAsync 是异步的,所以它改变了一点使用它的方式。

希望能帮助到你。

于 2014-11-09T02:09:26.563 回答
1

简短的回答是,一旦 AsyncTask 启动,您就无法取消它。您可以做的是在doInBackGround()其中插入一个循环,该循环将检查它是否在将来某个时间设置为 true - 从函数返回一个值(如果您定义了它isCancelled(),它将依次调用);onPostExecute()

请注意,仅仅因为您无法停止 AsyncTask 并不意味着如果内存不足,操作系统将不会取消它。如果您在 AsyncTask 中执行基本任务(您希望 100% 执行的任务),您应该牢记这一点。如果是这样,最好使用Service - 一个根据需要由操作系统自动终止和重新启动的组件。

于 2013-06-26T08:49:36.130 回答
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;
    }

    // ...

}

礼貌,有关更多详细信息,请参阅此答案。

于 2013-06-26T08:46:00.600 回答