Say I am running
new CommentList().execute(url);
If I am in the doInBackground method and I catch a Null value and inside that null value exception I try running the same one again:
new CommentList().execute(url);
Will it stop running the first one?
Can I do this:
if (result == null) {
cancel(true);
}
#Override
protected void onCancelled() {
new CommentList().execute(commentlinkurl);
}
Basically I don't want the onPostExecute to run if it gets cancelled.
This is not a good idea. You should not be creating new task on a non-UI thread (from within doInBackground method).
From docs:
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.)
Edit based on comments:
You can however start the task again inside onPostExecute or onCancelled method. You can simply return some specific result to it from doInBackground or save your Throwable in the AsyncTask member variable to analyze it further:
protected void onPostExecute(Something something) {
if(something == null){
// safe to start new execute task here
}
// or
if(mException instanceof TemporaryIssueException){
// safe to start new execute task here
}
}
Related
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
Above code snippet is from the AsyncTask source code implementing the SerialExcutor, But I dont understand how exactly it works.
when a new task arrives, it is put into the end of a ArrayDeque, the task on the top of the ArrayDeque get executed only when there is no other task is being executed currently. (when mActive == null).
So if a task is being executed when a new task arrive, there is nothing will be triggered, when the task finish executing, how the ArrayDeque know pop the next task on the top to execute it???
Tasks are executed on a separate thread by THREAD_POOL_EXECUTOR that takes in a Runnable. In this Runnable, when the running task finishes for some reason, scheduleNext() is called in the finally block. If there are any tasks in the queue, the first one will be executed, otherwise the executor will be idle until the next call to execute(). Also, synchronized ensures that execute() and scheduleNext() cannot be run in separate threads at the same time.
lets delve into the SerialExecutor class. In this class we have final
ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
This actually works as a serializer of the different requests at different threads. This is an example of Half Sync Half Async pattern.
Now lets examine how the serial executor does this. Please have a look at the portion of the code of the SerialExecutor which is written as
if (mActive == null) {
scheduleNext();
}
So when the execute is first called on the Asynctask, this code is executed on the main thread (as mActive will be initialized to NULL) and hence it will take us to the scheduleNext() function.
The ScheduleNext() function has been written as follows:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
So in the schedulenext() function we initialize the mActive with the Runnable object which we have already inserted at the end of the dequeue. This Runnable object (which is nothing but the mActive) then is executed on a thread taken from the threadpool. In that thread, then "finally "block gets executed.
Now there are two scenarios.
Another AsyncTask instance has been created and we call the execute method on it when the first task is being executed.
Execute method is called for the second time on a same instance of the AsyncTask when the first task is getting executed.
Scenario I : If we look at the execute function of the SerialExecutor, we will find that we actually create a new runnable thread (Say thread t) for processing the background task.
Inside that thread t, we run the run function of the mActive. But as it is in the try block, the finally will be executed only after the background task is finished in that thread. (Remember both try and finally are happening inside the context of t).
Inside finally block, when we call the scheduleNext function, the mActive becomes NULL because we have already emptied the queue. However, if another instance of the same AsyncTask is created and we call execute on them, the execute function of these AsyncTask won’t be executed because of the synchronization keyword before execute and also because the SERIAL_EXECUTOR is a static instance (hence all the objects of the same class will share the same instance… its an example of class level locking) I mean no instance of the same AsyncTask class can preempt the execute function (and as a result the background task that is running in thread t). what it all means that there will be only one active thread running the task. This thread may not be the same for different tasks, but only one thread at a time will execute the task. hence the later tasks will be executed one after another only when the first task completes, that is why it is called SerialExecutor.
Scenario II: In this case we will get an exception error. To understand why the execute function cannot be called more than once on the same Asynctask object, please have a look at the below code snippet taken from executeOnExecutor in AsyncTask.java especially in the below mentioned portion:
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
As from the above code snippet it becomes clear that if we call execute function twice when a task is in the running status it throws an IllegalStateException saying “Cannot execute task: the task is already running.”.
You can read my discussion on AsyncTask internals
https://docs.google.com/document/d/1_zihWXAwgTAdJc013-bOLUHPMrjeUBZnDuPkzMxEEj0/edit?usp=sharing
Should I write smth like that
return task.exec(session, state).get(json_timeout, TimeUnit.MILLISECONDS);
Or I can do like this
task.exec(session, state, result);
return result;
A have already read all documentation that I found, but failed to find an answer. My bad...
Do not use get(). It will block the ui thread until asynctask finishes execution which no longer makes it asynchronous.
Use execute and to invoke asynctask
new task().exec(session, state, result);
Also you can pass the params to the constructor of asynctask or to doInbackground()
http://developer.android.com/reference/android/os/AsyncTask.html
public final Result get ()
Added in API level 3
Waits if necessary for the computation to complete, and then retrieves its result.
You can make your asynctask an inner class of your activity class and update ui in onPostExecute.
If asynctask is in a different file then you can use interface.
How do I return a boolean from AsyncTask?
AsyncTask#get() will block the calling thread.
AsyncTask#execute() will run in a separate thread and deliver the Result in onPostExecute(...).
I would recommend against using the get() method except in special cases like testing. The whole purpose of the AsyncTask is to execute some long-running operation in doInBackground() then handle the result once it's finished.
One example of normal AsyncTask execution would look like:
Task task = new Task(){
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
//handle your result here
}
};
task.execute();
I am running a task in back ground, and returning a result out of it.The result is coming null as its returned before the async task completes.how can i resolve it
public Result CallServer(String zObjectNameP, String zMethodNameP, String QueryStringP)
{
aResultM=new Result();
MainAynscTask asyncTask = new MainAynscTask();
try
{
asyncTask.execute(zObjectNameP,zMethodNameP,QueryStringP);
}
catch(Exception ex)
{
}
return aResultM;
}
The 4 steps
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
Use a Handler
In your activity
mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
String s=(String)msg.obj;
tv.setText("Result = "+s);
}
};
In your onPostexecute
protected void onPostExecute(String result)//result returned in doInbackground
{
pd.dismiss();
if(result != null)
{
Message msg=new Message();
msg.obj=result.toString();
mHandler.sendMessage(msg);
}
}
I created an async task to call my server to get data from DB.
I need to process the result returned from http server call.
From my activity i calling the async task in many places. so i cant use member variable to access the result. is there any way to do?
public Result CallServer(String params)
{
try
{
new MainAynscTask().execute(params);
}
catch(Exception ex)
{
ex.printStackTrace();
}
return aResultM;//Need to get back the result
}
private class MainAynscTask extends AsyncTask<String, Void, Result> {
#Override
protected Result doInBackground(String... ParamsP) {
//calling server codes
return aResultL;
}
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
//how i will pass this result where i called this task?
}
Try to call the get() method of AsyncTask after you call the execute() method. This works for me
http://developer.android.com/reference/android/os/AsyncTask.html#get()
public Result CallServer(String params)
{
try
{
MainAynscTask task = new MainAynscTask();
task.execute(params);
Result aResultM = task.get(); //Add this
}
catch(Exception ex)
{
ex.printStackTrace();
}
return aResultM;//Need to get back the result
}
...
...
There are two ways i can suggest -
onPostExecute(Result) in AsyncTask. See http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute(Result)
Send a broadcast with the result as an extra.
AsyncTask is an asynchronous task so it does NOT make sense to return the result to the caller. Rather handle the result in onPostExecute() like setting the value to TextView etc. Or send a broadcast so that some other listener can handle the result.
Here's how I got around this:
1) Create an interface class that defines a signature for a method to execute on completion:
public interface AsyncIfc {
public void onComplete();
}
2) Set a property on your AsyncTask class to hold the delegate method:
public AsyncIfc completionCode;
3) Trigger the delegate from onPostExecute() in the AsyncTask:
completionCode.onComplete();
4) From your calling logic, set the delegate property to an anonymous method:
task.completionCode = new AsyncIfc() {
#Override
public void onComplete() {
// Any logic you want to happen after execution
}
};
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
Use a handler
In your activity
Handler mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
String s=(String)msg.obj;
tv.setText(s);
}
};
//result is soap object in this case.
protected void onPostExecute(SoapObject result) {
pd.dismiss();
if(result != null) {
Message msg=new Message();
msg.obj=result.getProperty(0).toString();
mHandler.sendMessage(msg);
}
I want to cancel the Async Task on the particular condition.
I am doing the following stuff:
MyService.java
....
if(condition){
asyncTask.cancel(true); // its return the true as well
}
...
MyAsynTask.java
...
protected Object doInBackground(Object... x) {
while (/* condition */) {
// work...
if (isCancelled()){ // Here task goes in to wait state
break;
}
else{
//continue to download file
}
}
return null;
}
...
Using DDMS I found that task goes into wait State. Any suggestion to resolve this issue will be highly appreciated.
Thanks,
Yuvi
AsyncTask is a piece of work for PoolExecutor. When you execute your first task Executor creates first thread and executes your task on it. After task execution is finished the thread is not deleted. It starts waiting for a new task.
So it is normal to see AsyncTask thread in wait state.
P.S. It's better not to use AsyncTask for longtime operation. Use your own executor or thread.
P.P.S. AsyncTask uses single thread executor since 4.x. Be careful )
after you explicitly call asyncTask.cancel(true);, the onCancelled() method is called. Try overriding the following method:
#Override
protected void onCancelled() {
//what you want to do when the task was cancelled.
}