How does AsyncTask SerialExecutor work? - android

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

Related

Does removeCallbacksAndMessages serialize?

For purposes of example, suppose I have two methods that will be called asynchronously, doDelayEventA and doEventB. To simplify things we will assume that doDelayEventA will be called once and doEventB will be called once.
doDelayEventA starts a timer (actually a Handler) and doEventB kills it (if it is still pending).
private Handler mTimer;
public void doDelayEventA(){
mTimer = new Handler();
mTimer.postDelayed(new Runnable() {
public void run() {
Log.d(LOG_TAG, "Event A: We want this to happen FIRST or not at all");
}
}, 1000);
}
public void doEventB(){
if (mTimer != null) {
mTimer.removeCallbacksAndMessages(null);
}
Log.d(LOG_TAG, "Event B: If it happens, it should ALWAYS happen LAST");
}
The question: Does the removeCallbacksAndMessages force a serialization, that is, does it block execution until the runnable has been removed or executed? I would like to insure that given both methods are called, The EventA Log statement will always execute first or not at all ahead of the EventB Log statement.
Does the removeCallbacksAndMessages force a serialization, that is, does it block execution until the runnable has been removed or executed?
If doEventB() is executed on the main application thread (or, more accurately, on the same thread that the Handler is tied to), you should be in OK shape. Handler uses a MessageQueue for this, and MessageQueue does remove the message synchronously, at least in Android 7.1.
However:
This is not documented behavior AFAIK, so there's some risk that other versions of Android behave differently
If you call doEventB() on a different thread, while there should be no thread synchronization issues (e.g., ConcurrentModificationException), you may get a race condition: doEventB() is called on one thread, the Runnable starts executing on another thread, and the doEventB() log message happens before the Runnable log message

Communication between main thread and worker threads in android

In my very first android project, I do some data manipulation, so I use multi-threading approach.
In MainActivity, I created multiple Runnable object and use ExecutorService to run all the threads. As my understanding, all threads are put in message queue and executed in turn. And the because the main thread is already in the queue, it will be executed before starting other threads. Is there any way that I can make the main thread wait for other threads to finish and then continue?
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//call MyFunction here
}
private List<Pair[]> myFunction(int dataInput) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(12);
MyTask MyTask = new MyTask();
for (int i = 0; i < gallerySize; ++i) {
final int index = i;
Runnable runnable = MyTask.runLongOperationWithThread(new MyTask.DataCallback(){
#Override
public void onSuccess(double[] scores) {
// get data back to main thread
}
#Override
public void onError(Exception ex) {
//TODO: log this error out to file
}
});
executorService.execute(runnable);
}
// try to get back all data from multi threading and do some operations
return returnList;
}
Do Looper and Handler help in this case?
And please correct me if I have any misunderstanding in android concept and threading.
Thanks.
In Android, stopping main thread is discouraged. The system will tell the user that the app is not responding. However, you can "notify" the main thread that the background thread has finished its work. Once the main thread knows this, it will do something. It is common in Android, it is what AsyncTask for.
However, AsyncTask is used for a simple one thread. In your case, one of the solution is to combine ExecutorService and AsyncTask. In doInBackground method of AsyncTask instance you make, use ExecutorService like usual, and wait it to finish by either shutdown(); awaitTermination() or invokeAll(). Read this question/answer for more information about how to wait ExecutorService to finish.
private class WrappingTask extends AsyncTask<Void, Void, Exception> {
protected Exception doInBackground(Void... args) {
ExecutorService taskExecutor = Executors.newFixedThreadPool(12);
for (. . .) {
taskExecutor.execute(new MyTask(. . .));
}
taskExecutor.shutdown();
try {
taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
. . .
return e;
}
return null;
}
protected void onPostExecute(Exception error) {
// Notify the user that the task has finished or do anything else
// and handle error
}
}
In case of long running task
AsyncTask is a handy class to make threading and communicating (to main thread) easier. The problem for long running task is that the user can leave the Activity (and then come again), or there is an incoming call, etc. If you don't handle this Activity lifecycle with care, it is so "dangerous", AsyncTask does not handle this.
Long running task should be run in a Service. Note that Service is also run in the main thread, so the approach would be the same, unless you use IntentService. In case of IntentService, just execute all of the threads (formerly in doInBackground) in the onHandleIntent method and wait it there, this method is called on a worker thread.
Communicating Service with Activity and maintaining consistency of Activity's state through its lifecycle is a long story. You better read the documentation in "a full concentration" with a cup of coffee :D. This might helps:
Managing the Activity Lifecycle
Best Practices for Background Jobs

Execute two sequences of AsyncTasks serially

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();
}

Android: Forcing the main run loop to run before current thread of execution is complete

On iOS, if I want my current thread of execution to wait (ie. block) and the main loop to run so that the thread of execution next in the main queue can execute, I invoke:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
How would I go about doing the equivalent on Android?
This is indeed possible to do in Android. Shachar's answer is on the right track. The problem is not that the main loop will block (unless the code was executed on the main thread, but that's not what the question is proposing). The problem is that the other thread doesn't block, but is simply looping and burning CPU cycles in the while loop. Here is a blocking run on main method I use in my app:
/**
* Runs the runnable on the main UI thread. If called from a thread other than the UI thread,
* this method will block the calling thread and return only after the runnable has completed
* execution on the main UI thread.
* #param runnable Runnable to run on the main UI thread
*/
public static void blockingRunOnMain(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) { // Already on UI thread run immediately
runnable.run();
}
else { // Queue to run on UI thread
final MainRunMonitor lock = new MainRunMonitor();
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(runnable);
// Task to notify calling thread when runnable complete
mainHandler.post(new Runnable() {
#Override
public void run() {
synchronized (lock) {
lock.mRunComplete = true;
lock.notify();
}
}
});
// Block calling thread until runnable completed on UI thread
boolean interrupted = false;
try {
synchronized (lock) {
while (!lock.mRunComplete) {
try {
lock.wait();
} catch (InterruptedException e) {
// Received interrupt signal, but still haven't been notified, continue waiting
interrupted = true;
}
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt(); // Restore interrupt to be used higher on call stack (we're not using it to interrupt this task)
}
}
}
}
MainRunMonitor is a simple class, in my case a private inner class to the class that implements blockingRunOnMain():
/**
* Monitor to lock calling thread while code is executed on UI thread.
*/
private static class MainRunMonitor {
private boolean mRunComplete = false;
}
blockingRunOnMain() is used by passing it a Runnable to run on the main thread:
blockingRunOnMain(new Runnable() {
#Override
public void run() {
workToDoSynchronouslyOnMain();
}
});
The first part of the blockingRunOnMain() method checks if the method is being called from the main thread and if so, simply executes the code immediately. Since the function of blockingRunOnMain() is to synchronously run the Runnable code before the method returns, this will have this same result even if called from the main thread itself.
If the method is called from a thread other than the main thread, we then post the Runnable to a Handler which is bound to the main thread's Looper. After posting the Runnable parameter, we then post another Runnable that will execute after the Runnable parameter completes execution, since the Handler executes posted Messages and Runnables in order. This second Runnable serves to notify the blocked thread that the work has been completed on the main thread.
After posting the second Runnable we now block the background thread and wait until we're notified. It's important to synchronize the operations performed on lock so that the operations are atomic on each thread.
The background thread calls wait() on the monitor and waits until mRunComplete == true. If it gets an InterruptedException, it's important to continue waiting and restore the interrupted state of the thread after we're done, since we're not using the interrupt mechanism ourselves to cancel our task, restoring it allows another method higher on the call stack to handle the interrupt. See "Dealing with InterruptedException".
When the Runnable parameter has completed execution and the second posted Runnable executes, it simply sets mRunComplete to true and notifies the blocked thread to continue execution, which finding mRunComplete == true now returns from blockingRunOnMain(), having executed the Runnable parameter synchronously on the main UI thread.
One short workaround is to have a boolean that is changed by the next main thread loop.
running on main thread can be done with runOnUIthread (or getting the main looper yourself)
moving to the next loop can b easely done with handler.postDelayed(Runnable run, long delayMills), and a no-time delay.
so you could do this:
nextMainLoopDone = false;//This should be changed to a thread safe boolean, could use AtomicBoolean
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
nextMainLoopDone = true;
}
}, 1/* delay for no time, just to next loop*/);
while(!nextMainLoopDone) {
;
}
I'm sorry to disappoint you, but it is not possible to do what you're asking for in Android.

Quick AsyncTask doInBackgroun Q

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
}
}

Categories

Resources