Question list:
- what is a intentService.
- HandlerThread.
- AsyncTask
- Thread Pool
HandlerThread : its internal.
Internally HandlerThread only cotains 2 major pieces :
- priority,
- Looper, fleshed when run() get called.
HandlerThread
The name of HandlerThread is misleading, it really means "A looper thread that can handle your message when you call handler.post(msg)",However you need to hook into the looper first by creating your Handler using handler(Looper) constructor.
This is how Your Thread should communicate with A HandlerThread.
- Since underlaying of HanlerThread uses Looper and MessageQueue, so it execution of messages are guaranteed to be sequential.
To Terminate a HandlerThread, you can call quit()/quitSafely() on one instance of HandlerThread, you can also post a runnale into the queue and let Looper terminate itself, it is what quit() is doing exactly internally.
// an alternative to terminate handler thread. handler.post(new Runnable() { @Override public void run() { Looper.myLooper().quit(); } }); // the implementation of quit(); public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; }
For most of the time, you can expose the handler to let your handler open for post message, the other cases you want to hide How the Thread is implemented, you can hide the Handler as a member inside HandlerThread's subclass, on onLooperPrepared() callback, initialize the mHandler, and provide several APIs to simply post a message to execute.
Service
AsyncTask
What is inside a AsyncTask: it has 2 executors, one serial(the default), one concurrent, this is where all the tasks been executed. it also has a internal handler, this handler points to caller's Looper and Message Queue, so the update and post result message will be sent to caller's thread, in most of the case, will be sent to UI thread, so the result/update is posted on UI thread.
Notice that the internal handler and the looper attached to it is for post update and result, the threads that executors hold has no attachment to the lifecycle of actvity and other component.
doInBackground() is called in the worker runnable executed by FutureTask&Executor, in the implementation of doInbackground, you can update progress
IntentService
Internally IntentService holds two key elements: message passing, work thread. message passing : handler object, when some client start/bind to an intentService, a message is posted by this handler, to execute on worker thread. worker thread : Handler thread, which means the messages sent here is executed sequentially.
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
to implement a intentService, you only need to override onHandleIntent(), the drawback of a IntentService is that right after onHandleIntent get called, the service will try to stop itself, your service get some intent to handle frequently, this means your service can be killed and recreate very often.\
MesssageQueue
At the core of MessageQueue designing, Looper uses a epoll fd to monitor a eventFd, which only write/read one char to indicates that there is something to read from the MessageQueue.
Linux's epoll by default using level trigger. so if the monitored file contains readable, it will return from epoll_wait. And Message and epoll in Looper(native) use timestamp to make sure you wait long enough to pull next event.
Here is how the looper thread been blocked and unblocked by underlying native looper.
Following section will discuss what message is returned exactly.
Message in MessageQueue.
When you use Handler to enqueue a message, it is in general a synchronous message, i.e. all the messages are handled in the order they were put into the queue.
However when the Handler is initialized with asynchronous enabled, the message been inserted inside the queue is marked as asynchronous.
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
if you check the code for enqueMessage, you will notice that all the message is order by the timestamp when is is prepared.
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
According to the document, this type of message is not constrained by the barrier message put into the queue.