多次执行AsyncTask


127

在我的活动中,我使用从AsyncTask扩展的类和作为该AsyncTask实例的参数。当我打电话时,mInstanceOfAT.execute("")一切都很好。但是,当我按下再次调用AsyncTask的更新按钮时,应用程序崩溃(以防网络作业无法正常工作)。然后出现原因,显示异常

无法执行任务:任务已经执行(一个任务只能执行一次)

我尝试为Asyctask实例调用cancel(true),但它也不起作用。到目前为止,唯一的解决方案是创建Asyntask的新实例。那是正确的方法吗?

谢谢。

Answers:


217

AsyncTask 实例只能使用一次。

而是像这样调用您的任务 new MyAsyncTask().execute("");

从AsyncTask API文档中:

穿线规则

为了使此类正常工作,必须遵循一些线程规则:

  • 必须在UI线程上创建任务实例。
  • 必须在UI线程上调用execute(Params ...)。
  • 不要手动调用onPreExecute(),onPostExecute(Result),doInBackground(Params ...),onProgressUpdate(Progress ...)。
  • 该任务只能执行一次(如果尝试第二次执行,则将引发异常。)

2
我说过的话,那是唯一的可能性吗?原因我想保存内存,而不是创建一个新对象。
Dayerman 2011年


@StevePrentice:如果我每隔x秒用new task()。execute(param)创建一个任务实例,以将数据发送到服务器,那么执行完成后垃圾收集器如何释放内存?
Ant4res

3
@ Ant4res,只要您不引用异步任务实例,GC就会释放内存。但是,如果您有正在进行的后台任务,则可以考虑在doInBackground内部的循环中执行此任务,并调用publishProgress来更新进度。或者,另一种方法是将您的任务放入后台线程。这里有很多不同的方法,但是如果没有细节,就不能推荐一种方法。
Steve Prentice 2013年

28

史蒂夫·普伦蒂斯(Steve Prentice)的回答中很好地阐明了触发和忘记ASyncTask实例的原因-但是,尽管您限制执行ASyncTask的次数,但是可以在线程运行时自由地执行所需的操作。 。

将您的可执行代码放在doInBackground()中的循环中,并使用并发锁来触发每次执行。您可以使用publishProgress()/ onProgressUpdate()检索结果。

例:

class GetDataFromServerTask extends AsyncTask<Input, Result, Void> {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition tryAgain = lock.newCondition();
    private volatile boolean finished = false;

    @Override
    protected Void doInBackground(Input... params) {

        lock.lockInterruptibly();

        do { 
            // This is the bulk of our task, request the data, and put in "result"
            Result result = ....

            // Return it to the activity thread using publishProgress()
            publishProgress(result);

            // At the end, we acquire a lock that will delay
            // the next execution until runAgain() is called..
            tryAgain.await();

        } while(!finished);

        lock.unlock();
    }

    @Override
    protected void onProgressUpdate(Result... result) 
    {
        // Treat this like onPostExecute(), do something with result

        // This is an example...
        if (result != whatWeWant && userWantsToTryAgain()) {
            runAgain();
        }
    }

    public void runAgain() {
        // Call this to request data from the server again
        tryAgain.signal();
    }

    public void terminateTask() {
        // The task will only finish when we call this method
        finished = true;
        lock.unlock();
    }

    @Override
    protected void onCancelled() {
        // Make sure we clean up if the task is killed
        terminateTask();
    }
}

当然,这比ASyncTask的传统用法稍微复杂一些,您放弃了使用publishProgress()进行实际进度报告。但是,如果您关心内存,则此方法将确保运行时堆中仅保留一个ASyncTask。


但是关键是我不想在运行时重新执行Asyntask,但是由于该任务已经完成并且没有按预期方式接收数据,然后再次调用它。
Dayerman 2011年

实际上,您仅以这种方式执行一次ASyncTask,并且您可以在onPublishProgress方法内检查数据是否正确(或将检查委托给其他地方)。不久前,我将这种模式用于类似的问题(很多任务很快连续被触发,这可能会增加堆大小)。
seanhodges 2011年

但是,如果那一刻服务器没有响应,我想在10秒钟后再试一次,那该怎么办?AsyncTask已经完成,很
糟糕

我添加了一些示例代码来描述我的意思。只有对结果满意并调用“ terminateTask()”后,ASyncTask才会完成。
seanhodges 2011年

1
如果你IllegalMonitorStateExceptionrunAgain(通过所谓的onProgressUpdate)看到这样的回答:stackoverflow.com/a/42646476/2711811。它建议(并为我工作)signal()需要用lock/ 包围unlock。这可能与publishProgress呼叫的时间有关onProgressUpdate
安迪

2

我遇到过同样的问题。就我而言,我有我想在做任务onCreate()onResume()。所以我将我的Asynctask设为静态,并从中获取实例。现在我们仍然有同样的问题。

所以我在onPostExecute()中所做的是这样的:

instance = null;

请记住,我在静态getInstance方法中检查到我的实例不为null,否则我将其创建:

if (instance == null){
    instance = new Task();
}
return instance;

postExecute中的方法将清空实例并重新创建它。当然这可以在课外完成。


1

我已经将轮换任务设为静态,这有助于我在轮换更改时将其附加,分离和重新附加到UI线程。但是回到您的问题,我要做的是创建一个标志以查看线程是否正在运行。当您要重新启动线程时,如果检查旋转任务是否正在运行,请检查警告。如果不是,我将其设置为空,然后创建一个新的,它将解决您所看到的错误。此外,成功完成后,我会取消已完成的轮换感知任务,以便准备再次进行。


0

是的,这是真的,医生说只能执行一个Asyntask。

每次需要使用它时,都必须实例化:

// Any time if you need to call her
final FirmwareDownload fDownload = new FirmwareDownload();
fDownload.execute("your parameter");

static class FirmwareDownload extends AsyncTask<String, String, String> {
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.