Execute two sequences of AsyncTasks serially - android

There are 2 sequences of AsyncTasks:
A -> B -> C
D -> E -> F
That is in onPostExecute, task A starts task B, which in onPostExecute starts task C. The same goes for the second sequence.
If the sequences are started like this:
new A().execute();
// at a later time, but while A is still executing
new D().execute();
Are there and what are the ways to ensure task D not started until task C has finished?

There is a framework that allows to "sequence" and "chain" async calls: https://github.com/BoltsFramework/Bolts-Android
For a more advanced execution control, have a look at RxJava: https://github.com/ReactiveX/RxJava
Also, You can avoid such issues in future be separating functionality from its execution mode. That is, Write the code in simple methods of respective classes, don't make it "Tasks". Create Tasks separately as pure containers of some method calls, that can call any sequence of given functions from background, or foreground.

A simple queue will work for now:
public class OperationsQueue {
private Operation ongoingOperation;
private Queue<Operation> queue = new LinkedList<>();
public void execute(Operation operation) {
if (ongoingOperation != null)
queue.add(operation);
else {
ongoingOperation = operation;
operation.setOnFinishedCallback(() -> operationFinished(operation));
operation.execute();
}
}
private void operationFinished(Operation operation) {
ongoingOperation = null;
Operation nextOperation = queue.poll();
if (nextOperation != null)
execute(nextOperation);
}
}
public abstract class Operation {
protected Runnable onFinished = () -> { };
public abstract void execute();
public void setOnFinishedCallback(Runnable onFinished) {
this.onFinished = onFinished;
}
}
Concrete implementations of Operation need to start the first async task in the sequence and the last async task must call onFinished.
That is in case of this sequence:
A -> B
An operation passes a callback to async task A which it passes to async tasks it starts so that the last task B calls it in its onPostExecute:
class SomeOperation extends Operation {
public void execute() {
new A(() -> onFinished()).execute();
}
}
class A extends AsyncTask<Void, Void, Void> {
//...
public Runnable onSuccess;
public A(Runnable onSuccess) {
this.onSuccess = onSuccess;
}
onPostExecute() {
new B(onSuccess).execute();
}
}
class B extends AsyncTask<Void, Void, Void> {
//...
public Runnable onSuccess;
public B(Runnable onSuccess) {
this.onSuccess = onSuccess;
}
onPostExecute() {
onSuccess.run();
}
}
SomeOperation and its underlying tasks may now be executed serially:
OperationsQueue queue = new OperationsQueue();
queue.execute(new SomeOperation());
queue.execute(new SomeOperation());
Task A of the second SomeOperation will not execute until task B on the first finishes.

Actually in the latest Android versions AsyncTask is already running on single thread pool executor. But to not depend on default behavior you can run it on you own Executor. So you can create single thread executor:
Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
and just run your AsyncTasks on it one by one:
synchronize (tasksLock) {
new A().executeOnExecutor(singleThreadExecutor);
new B().executeOnExecutor(singleThreadExecutor);
new C().executeOnExecutor(singleThreadExecutor);
}
// on some your event
synchronize (tasksLock) {
new D().executeOnExecutor(singleThreadExecutor);
new E().executeOnExecutor(singleThreadExecutor);
new F().executeOnExecutor(singleThreadExecutor);
}
Executor guarantees that they will be started in the order executeOnExecutor was called.

You can consider that as a queue of tasks. If you start AsyncTask normally (call task.execute() only) --> every task will be pushed into a queue in a thread off UI thread. Which called first, will be executed first. Therefore, your tasks will be start one by one and which is started first based on which task.execute() called first. If you want to call 2 sequences of tasks in 2 difference threads, I suggest that you use ExecutorService.
There some important notes from early version of android OS:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.

Try this
*put the task A to a variable on executing it.
A taskA = new A();
taskA.execute();
* And later you have to check whether the task A is running by
if (taskA != null && taskA.getStatus() == A.Status.RUNNING) {
// Task A still running
return;
}else{
// Task A completed
new D().execute();
}

Related

Hold calling thread until multiple asynctask finishes

I have a background thread which calls 3 asynctasks to perform tasks simultaneously. The calling thread acts as a Queue for 3 sets of these tasks.
So basically I need to call 3 asynctasks simultaneously and once they are completed I want to call the next three tasks on the queue and repeat.
However I am having trouble pausing the caller thread until the three asynctask finishes. As a result the next three tasks in the queue start running before the previous three tasks are completed.
So is there anyway to hold the caller thread until the asynctasks are completed. I know that you can user .get() in asynctask but it will not enable the three asynctasks to run simultaneously.
Following code is rather a pseudocode of the idea. Basically, you'll declare an interface which will check for firing next three AsyncTasks. You'll also need to maintain a counter to see if the number of received response from AsyncTask is multiplied by 3. If it is then you can trigger next three AsyncTasks.
public interface OnRunNextThree{
void runNextThreeTasks();
}
public class MainClass extends Activity implements OnRunNextThree {
private int asyncTasksCounter = 0;
public void onCreate() {
//Initiate and run first three of your DownloadFilesTask AsyncTasks
// ...
}
public void runNextThreeTasks() {
if (asyncTasksCounter % 3 == 0) {
// you can execute next three of your DownloadFilesTask AsyncTasks now
// ...
} else {
// Otherwise, since we have got response from one of our previously
// initiated AsyncTasks so let's update the counter value by one.
asyncTasksCounter++;
}
}
private class DownloadFilesTask extends AsyncTask<Void, Void, Void> {
private OnRunNextThree onRunNextThree;
public DownloadFilesTask(OnRunNextThree onRunNextThree) {
this.onRunNextThree = onRunNextThree;
}
protected Void doInBackground(Void... voids) {
// Do whatever you need to do in background
return null;
}
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
//Got the result. Great! Now trigger the interface.
this.onRunNextThree.runNextThreeTasks();
}
}
}
Async tasks are meant to do things asynchronously.... so this can't be done in a straight forward way...
Even if you manage to do this, it basically defeats the whole point of asynchronous operation.
You should look for a Synchronous network operation.
Check out Volley... It is a google library specially made for network operations and it supports Synchronous operations
http://www.truiton.com/2015/02/android-volley-making-synchronous-request/
There are many other libraries available ... Retrofit is one other good library..

Android AsyncTask waiting/queued

I have a class extending an AsyncTask that sends messages to a WCF web service. Simple messages one by one will work fine, but if I send a message on a new thread that takes 30 seconds to complete, then midway through that I send a quick request it won't execute the AsyncTask until the long one has returned.
I thought the whole idea of AsyncTask was these two messages would run on different threads and therefore wouldn't stack?
Here is my code:
private class RunnableTask extends AsyncTask<RunnableObj, RunnableObj, RunnableObj> {
#Override
protected RunnableObj doInBackground(RunnableObj... params) {
try {
if (params[0].requestBody != (null)) {
params[0].request.body(new JSONObject(params[0].requestBody));
}
params[0].request.asVoid();
return params[0];
}
catch (Throwable e) {
params[0].handler.onFailure(e);
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(RunnableObj runnableObj) {
super.onPostExecute(runnableObj);
runnableObj.handler.onSuccess();
}
}
This is my AsyncTask above.
public void put(final String urlStr, final String requestBody, final HttpResponseHandler httpResponseHandler) {
RunnableObj obj = new RunnableObj();
obj.handler = httpResponseHandler;
obj.request = webb.put(urlStr)
.header(ServiceConstants.SessionTokenHeader, MyApplication.getSessionToken())
.ensureSuccess();
obj.requestBody = requestBody;
new RunnableTask().execute(obj);
}
This is the method I use to call the Async.
As you can see in the method I use to call the service, I initialise a new instance of RunnableTask each time.
How it performs:
The long request will go to the web service and start it's 30 seconds of doing things.
10 seconds later my quick little PUT creates it's object, then the last thing the debugger shows is the break point on the "new RunnableTask().execute(obj);" line and then it just disappears.
20 seconds later the first line of my RunnableTasks doInBackground method will hit and it will perform the PUT.
Please can someone help? Or at least tell me I'm doing something very stupid..
You can execute multiple AsyncTask by using executeOnExecutor
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
new MyAsyncTask().execute();
}
For more check the AsyncTask documentation
According to the AsyncTask Docs:
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
If you truly want parallel execution, you can invoke
executeOnExecutor(java.util.concurrent.Executor, Object[]) with
THREAD_POOL_EXECUTOR.
As you can see, AsyncTasks currently only operate on a single background thread, meaning multiple queued tasks will have to fire one after another. If you would like to execute concurrent tasks, you'll want to follow the instructions above.

Asynctask and queue OR delay in execution

Scenario:
The user has a list of items, let's say 10 items. Each item has an Operation button, which calls an AsyncTask which makes a web call. When a call is made, the item displays a spinner during the execution of the task
Problem:
Some of the users abuse this, and press quickly more Operation buttons, quicklt one after another, executing the web calls too often. So I want to be able to somehow, execute each of the AsyncTasks one after another with a delay of 2 seconds between executions. I do not want to switch to something else from AsyncTask if possible. So basically if there are 3 Operation buttons pressed, the execution should be:
-> Operation 1
-> 2 seconds delay
-> Operation 2
-> 2 seconds delay
-> Operation 3
-> ....
What would be the best way to do this in Android?
LE:
I have just realized something, for executing my task I ran the following code:
myTask = new MyTask();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
myTask.execute();
}
Well, I've been using this code for a lot of time now, knowing that after honeycomb the tasks were not executed in parallel anymore without using an Executor. So it seems that only doing a simple myTask.execute() and adding a Thread.sleep() makes my AsyncTasks execute, one after another just as expected.
You will need to maintain a list of the operations that needs to be performed.
on click of the button add the task in the list, call a method which check the list for the task and executes it if there is no other task is running..
in onPostExecute method call the same method to check if there is any other task / operation that needs to be performed..
It may not be the full code you require... but may give you some idea..
public class TestActivity extends AppCompatActivity {
private static boolean isTaskRunning =false;
static ArrayList<CustomTask> customTaskList = new ArrayList();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
public void onBtnClick(View view)
{
// create custom task with required values and actions
CustomTask customTask = new CustomTask();
customTaskList.add(customTask);
checkAndExecuteTask();
}
private static void checkAndExecuteTask()
{
//checks if there is any task in the list and is there any other running task
if(customTaskList.size()>0 && !isTaskRunning) {
new MyAsync(customTaskList.get(0)).execute();
}
}
static class MyAsync extends AsyncTask<Void,Void,Void>
{
CustomTask currentCustomTask;
public MyAsync(CustomTask customTask)
{
currentCustomTask = customTask;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
isTaskRunning= true;
}
#Override
protected Void doInBackground(Void... voids) {
// do your stuff
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
customTaskList.remove(currentCustomTask);
isTaskRunning =false;
checkAndExecuteTask(); // task is completed so check for another task and execute (if any).
}
}
class CustomTask
{
// create class with required fields and method
}
}
There are a number of ways you can do this in android.
One way is to use a handler.
What you need to do is to, create a seperate thread and run handler.postDelayed in it.
private void startWebCall() {
Thread thread = new Thread() {
public void run() {
Looper.prepare();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Do your web calls here
handler.removeCallbacks(this);
Looper.myLooper().quit();
}
}, 2000);
Looper.loop();
}
};
thread.start();
}
You should call above method whenever user clicks a item.
Another way that I can think of is using an IntentService
An IntentService is a service that is used for doing asynchronous tasks in background. It maintains a queue of the tasks it needs to do. It is different from the above approach in the sense that it executes these tasks in a sequential order. So when you make requests to it to make web calls it will queue them, make the first call and then after it finishes it will make the second call. So the different web calls will not execute in parallel. They will execute in a sequential order but in a different thread. Also it is a service so it can run even in the background, i.e if user closes the app.
This is a good tutorial to get start with IntentService.
AsyncTaks should be generally avoided unless the work one needs to do is quite trivial. This blog explains its pitfalls.

How does AsyncTask SerialExecutor work?

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

AsyncTask stopped working when android:targetSdkVersion was changed from 9 to 15

public void OnBtnSendClick(View v)
{
byte[] bytes = ...; // array is assigned here
SendTask task = new SendTask();
task.execute(new byte[][] {bytes}); // this line is executed
}
private class SendTask extends AsyncTask<byte[], Void, String>
{
#Override
protected String doInBackground(byte[]... parameters)
{
Log.i(LOG_TAG, "SendTask.doInBackground started"); // this line is not executed
return "OK";
}
#Override
protected void onPostExecute(String result)
{
// ...
}
}
This code worked with android:targetSdkVersion="9" in the manifest. I changed it to android:targetSdkVersion="15", and now SendTask.doInBackground is not executed. In debugger I see that task.execute line is executed, but doInBackground is never called.
BTW, the program contains another AsyncTask, which is successfully running.
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
http://developer.android.com/reference/android/os/AsyncTask.html
On API >= 11 by default only one AsyncTask is executed at a time.
If you truly want parallel execution, you can invoke
executeOnExecutor(java.util.concurrent.Executor, Object[]) with
THREAD_POOL_EXECUTOR.
One way to overcome this issue is to create a BaseTask and make all your tasks extends it
public class BaseTask extends AsyncTask
{
...
public void fireInTheHole()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
this.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
this.execute();
}
...
}
Then when you need to execute it, simply call myTask.fireInTheHole() instead of myTask.execute()

Categories

Resources