Execute AsyncTask several times
In my Activity I use a class which extends from AsyncTask and a parameter which is an instance of that AsyncTask. When I call mInstanceOfAT.execute("")
everything is fine.
But the app crash when I press an update button which calls again the AsyncTask(In case the network job didnt work). Cause then appears an Exception which says
Cannot execute task: the task has already been executed (a task can be executed only once)
I have tried calling cancel(true) for the instance of the Asyctask, but i开发者_如何转开发t doesnt work either. The only solution so far it's to create new instances of the Asyntask. Is that the correct way?
Thanks.
AsyncTask
instances can only be used one time.
Instead, just call your task like new MyAsyncTask().execute("");
From the AsyncTask API docs:
Threading rules
There are a few threading rules that must be followed for this class to work properly:
- The task instance must be created on the UI thread.
- execute(Params...) must be invoked on the UI thread.
- Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
- The task can be executed only once (an exception will be thrown if a second execution is attempted.)
The reasons for fire-and-forget instances of ASyncTask are detailed pretty well in Steve Prentice's answer - However, whilst you are restricted on how many times you execute an ASyncTask, you are free to do what you like whilst the thread is running...
Put your executable code inside a loop within doInBackground() and use a concurrent lock to trigger each execution. You can retrieve the results using publishProgress()/onProgressUpdate().
Example:
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();
}
}
Of course, this is slightly more complicated than the traditional usage of ASyncTask, and you give up the use of publishProgress() for actual progress reporting. But if memory is your concern, then this approach will ensure only one ASyncTask remains in the heap at runtime.
I had the same issue. In my case i have a task i want to do in onCreate()
and in onResume(
). So i made my Asynctask static, and get the instance from it. Now we still have the same problem.
So what i did in the onPostExecute() is this:
instance = null;
Keeping in mind that i check in the static getInstance method that my instance isn't null, else i create it:
if (instance == null){
instance = new Task();
}
return instance;
The method in postExecute will empty the instance and recreate it. Of course this can be done outside the class.
I have made my rotation tasks static, which then helped me attach, detach, and reattach them to UI threads on rotation changes. But to go back to your question, what I do is create a flag to see if the thread is running. When you want to restart the thread I check if the rotation task is running if it is I toast a warning. If it is not, I make it null and then create a new one, which will work around the error you are seeing. Furthermore, upon successful completion I null out the completed rotation aware task so that it is ready to go again.
Yes it is true, the doc says that only can be executed one Asyntask.
Every time that you need to use it you have to instance:
// 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> {
}
精彩评论