Is there such a thing as task queue on Android? I know that it can be written by hand but is there a ready to use library for that?
There are four such queues with different priorities : high, default, low, and background.
A dispatch queue that is bound to the app's main thread and executes tasks serially on that thread. typealias dispatch_queue_global_t. A dispatch queue that executes tasks concurrently using threads from the global thread pool.
Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system.
In your comments, you mention that you were using NSTimer , a.k.a. Timer in Swift. If you want to stop a timer, call timer. invalidate() to stop it. Create a new NSTimer when you want to start it again.
I'm not sure if there would be a library for this one, as Android already provides the high-level building blocks for what you're trying to achieve.
If I understood you correctly, you want to post tasks from any thread to be queued and executed one-by-one on a dedicated thread. This is very much what Android Handler
is meant for.
Looper
.Looper
has an associated MessageQueue
Looper
underneath to enqueue and dequeue messages in a thread-safe manner into the Looper
's MessageQueue
.Handler
objects tied to a same Looper
. This is useful if you want to process different kinds of messages using different Handlers. In this case, you are guaranteed that only one of the Handlers will process a Message/Runnable for a given Looper
. The Looper
takes care of dispatching the Message to the right Handler
.golang
's buffered channel pattern), Handler
is just a high level class which lets you use this pattern easily.// BEGIN One-time Initialization
// Create a Handler thread
// This provides the looper for the Message Queue and
// will be processing all your messages (i.e. tasks).
handlerThread = new HandlerThread("SomeThreadName");
// Start the Handler Thread
// The thread will block (using the looper) until it
// receives a new message
handlerThread.start();
// Create a Message Handler which you can use to
// post and process messages
// The same Handler can also be used to post a Runnable which will get
// executed on handlerThread
handler = new CustomHandler(mHandlerThread.getLooper());
// END One-time Initialization
// Different ways to post a message to the Handler Thread
// These calls are thread-safe, can be called safely and
// concurrently from multiple threads without race conditions
handler.sendEmptyMessage(MESSAGE_ID_1);
handler.sendEmptyMessage(MESSAGE_ID_2);
handler.sendMessage(handler.obtainMessage(MESSAGE_ID_3, obj1));
handler.sendMessage(handler.obtainMessage(MESSAGE_ID_4, value, obj1));
handler.sendMessage(handler.obtainMessage(MESSAGE_ID_5, value1, valu2, obj1));
// Post a runnable on the Handler Thread
// This is thread-safe as well
// In fact all methods on the Handler class are thread-safe
handler.post(new Runnable() {
@Override
public void run() {
// Code to run on the Handler thread
}
});
// A skeleton implementation for CustomHandler
// NOTE: You can use the Handler class as-is without sub-classing it, if you
// intend to post just Runnables and NOT any messages
public class CustomHandler extends Handler {
public CustomHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
if (message != null) {
// Process the message
// The result can be sent back to the caller using a callback
// or alternatively, the caller could have passed a Handler
// argument, which the Handler Thread can post a message to
switch (message.what) {
case MESSAGE_ID_1:
// Some logic here
break;
case MESSAGE_ID_2:
// Some logic here
break;
case MESSAGE_ID_3:
// Some logic here
break;
case MESSAGE_ID_4:
// Some logic here
break;
case MESSAGE_ID_5:
// Some logic here
break;
// Add more message types here as required
}
}
}
}
// After you're done processing all messages and you
// want to exit the Handler Thread
// This will ensure that the queue does not accept any
// new messages, and all enqueued messages do get processed
handlerThread.quitSafely();
HandlerThread
in the above example, it is not mandatory to use it. You can even use the Looper
calls directly, i.e. Looper.prepare()
and Looper.loop()
to run your own message loop in a thread.Handler
if you do not intend to handle any messages.Handler
for each thread that needs to receive the message.Handler
to schedule message delivery and Runnable execution in the future as well.Android's framework internally uses Handler
extensively for managing component lifecycle events (onPause
, onResume
, etc.).
AsyncTask is another alternative to scheduling tasks on a different thread. . I won't go into too much detail of its implementation, as the Android developer documentation already describes it in detail.
I usually use AsyncTasks for tasks that I know I'll use a background thread for a long time (easily >= 100 ms at least). Some examples which fall into this category I can think of are Binder IPC, RPC calls, Network calls, Background downloads, etc.
On the other hand, Handler
is more tailored for situations focussed on processing more number of messages as quickly as possible. In other words avoid performing any blocking operation in handleMessage()
. You can write lock-free code easily using Handler
, it manages all the locking for you when enqueuing and dequeuing messages.
In fact AsyncTask
can be used in combination with Handler
by splitting the work into a fast part (taken care by Handler
) and a slow part (taken care by AsyncTask
).
PS: Although tangential to the question, if you're interested in the Message Queue paradigm; do take a look at LMAX Disruptor
, which is a high performance inter-thread Message Queue library. Their design document explains pretty well, which parts of the Message Queue, need locking/atomic access.
I've also looked around for something like GCD for Android. While Handlers and AsyncTasks are awesome the beauty of GCD (in my humble opinion) is that you can dispatch a workload on a background thread to do the heavy lifting. When the execution is done it i easy to execute the UI updates on the UI thread.
Since I did not find anything me and my school mate decided to create one of our own. You can find it at: ICDispatch on github
Basically all you need to do is to declare an Application class that extends ICDispatchApplication instead of Application and when you want to dispatch something you just call on
App.executeOn(int queue, ICBlock block);
Example:
App.executeOn(ICDispatch.NORMAL, new ICBlock(){
public void run(){
//do stuff...
App.executeOn(ICDispatch.MAIN, new ICBlock(){
public void run(){
//post result to UI thread.
}
}
}
});
The worst part is that there will be a lot of indentation. In order to minimize indentation you could use lambda notation:
App.executeOn(ICDispatch.NORMAL, ()->{
//do stuff...
//do some more...
//then even more
App.executeOn(ICDispatch.MAIN,() -> {
//Post result on UI thread.
}
});
At the moment ICDispatch supports LOW, NORMAL, HIGH, MAIN and CONCURRENT queueing. Features will be added as they are implemented.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With