Foreword
Android
The principle message mechanism is Android
one of the advanced knowledge must point in Android
the interview is one of the frequently asked questions. In Android
the sub-thread operation you are not directly View
need to switch to the main thread. This action then switches to relate to the Android
message mechanism, which is paper talk about Handler, Looper, MessageQueue, Message relations between them.
Handler
Handler
Playing in the message mechanism to send messages and process messages role, which we usually have the most contact classes.
How Handler process the message?
The following code shows Handler
how to handle the message. New Handler
objects, and rewrite the handleMessage, the processing logic within the associated methods, and in general the processing logic associated with the main thread. There are many constructors Handler, the following configuration is used in the main thread.
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what==1){
Toast.makeText(MainActivity.this,"handle message",Toast.LENGTH_LONG).show();
}
}
};
复制代码
Handler is how to send a message it?
Can understand the following code, Handler
object supports sending Message
and Runable
. Runable finally packed into Message
the callback
instance variable ( Handler
object processing message priority callback
logic), and Message
in the same way into the message queue. And each method has an associated deformation, supports delayed transmission, or some time in the future to send more.
//在消息池获取消息体,能达到消息重用,如果消息池没有消息,则新建消息
Message msg = handler.obtainMessage();
msg.what = 1;
//发送消息
handler.sendMessage(msg);
//发送空消息,参数会自动被包装msg.what=1
handler.sendEmptyMessage(1);
//未来的时间里发送消息
handler.sendEmptyMessageAtTime(1, 1000);
//延迟发送消息
handler.sendEmptyMessageDelayed(1, 1000);
msg = handler.obtainMessage();
msg.what = 2;
//将消息发送消息队列前面
handler.sendMessageAtFrontOfQueue(msg);
//发送任务,run方法内容将handler被处理。
handler.post(new Runnable() {
@Override
public void run() {
Log.i("Handler", "Runnable");
}
});
复制代码
If you normally use, we need only main process definition Hanlder
process contents of the message, the message is sent in sub-thread can be achieved by switching processes.
Looper
Looper
Is responsible for taking the message from the message queue cycle, is sent to Handler
the processing. Since only the message queue for storing messages, it is necessary to Looper
continue to take a message from the message queue Handler
. By default, all the threads do not own Looper
. If executed directly in the sub-thread Looper.loop
method, an exception occurs. Why is that the main thread without error? App start the process of creating ActivityThread
, the call will Looper.prepare
be created Looper
and MessageQueue
, and Looper.loop
open loop. The system is created in the main thread for us Looper
and MessageQueue
. So, to create a sub-line Handler
before, you need to call the Looper.prepare
method, created Looper
and MessageQueue
. IntentService
Is one such implementation. Click to see the point IntentService of knowledge.
MessageQueue
MessageQueue list in the form of internal organization, the main role is to store Message
. Creating Looper
time, it is automatically created MessageQueue
.
The relations between the formation mechanism of the Android news
Handler
When sending a message sends a message is inserted into MessageQueue
, and Looper
continued from the MessageQueue
take message distributed Handler
processing.
Source resolve
The building Handler
We look at Handler constructor code.
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//分析一
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//分析二
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
复制代码
Handler
There are many overloaded constructor, we often use the default constructor, it will eventually call the above constructor.
Analysis of a
By Looper.myLooper()
, by the Looper
instance. In the myLooper
implementation, by ThreadLocal
the get
way to get the. If ThreadLocal
not present Looper
, then the back null
. ThreaLocal
Here you can save the current thread simply understood as the private independent instances, other threads can not access. If ThreadLocal
there is no Looper
instance of the return null
. This is said earlier in the child thread is created Handler
before, we need to call the Looper.prepare
method. Otherwise it will be thrown RuntimeException
.
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
Analysis II
mQueue
Is a Looper
message queue, mCallBack
an interface is defined, for the callback message processing.
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
复制代码
Send Message Handler
Handler sends a message to all variants of the method eventually put in the following way to the message queue.
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
The most important thing is this enqueueMessage
method, the current Handler
object is set to Message
the target
variable. Then call queue queue
of enqueueMessage
methods.
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;
//如果队列为空或者插入message未来处理时间小于当前对头when
//则将当前消息设为队列头
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 {
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;
}
复制代码
In MessageQueue
the enqueueMessage
method, it will first check target
whether null
, message
whether it should be in use, whether to exit the current thread, the state of death. If an exception is thrown. If the current queue is empty or blocked, direct current Message
target is set to wake up the thread and the head of the queue. If not, then in accordance with Message
the object when
inserted into the appropriate queue position. Thus you can see, Handler
when the message is sent to the message on the queue.
Looper and create MessageQueue
Recall that child threads Handler, you need to call Looper
the static prepare
method.
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
复制代码
If the current thread already Looper
, alternative Looper will error. If not, new Looper
and to save ThreadLocal
in. new Looper
Very simple, just create a new one MessageQueue
, and holds the current thread.
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
复制代码
Looper is how to achieve the cycle of
In the call to the Looper.prepare
creation Looper
and MessageQueue
the object to be called Loop.loop
to start the cycle of distribution message queue messages.
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
//分析一
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
//分析二:
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
复制代码
Analysis of a: by unlimited for
loop to read the message queue. And MessageQueue
the next
internal method, according to the form of the linked list when
to return the order of attributes message
.
: Two analysis calling Message
the object target
's dipatchMessage
methods. Here target
it is to send a message Handler
object. In the Handler
object dipatchMessage
method, priority execution Message
object callback
method, that takes precedence when we send a message to Runable
the task of sending, if any. Otherwise, the detection Callback
object handleMessage
methods, and finally we rewrite the Hanlder
object's handleMessage
methods. Because Handler
not only have a default constructor, and can be passed Callback
, Looper
the constructor and the like.
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
Message reuse
By handler.obtainMessage
not new
get the message by way of example. Because the obtainMessage
method will first detect whether there is a message cell multiplexed message may not go to new
a message instance. The following is a Message class obtain
method.
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
复制代码
sPool
Type is Message
, by members of the internal variables next
, maintains a news pool. Although called the news pool, but through internal next
next constantly pointing Message
to the list maintained by the news pool, the default size is 50. In the chain sPool
case is not empty, to take the header Message
element and associated attributes are initialized.
Message objects are then put into the pool in a message when?
In Looper
the loop
method, and finally call Message
the recycleUnchecked
method
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
复制代码
Synchronization code block can be seen that the sPool
point to be currently recycled Message
objects, and Message
the next
point before the header.
to sum up
- In sub-threads
Handler
, you need to call theLooper.prepare
method, and then call theLooper.loop
method. - It maintains a message queue in the form of a linked list, and storing the message in accordance with the order of acquisition
when
in order of priority time. - By Handler, time-consuming operation in the child thread, the main thread update the UI.
IntentService
Scenario:HandlerTread
,AsyncTack
, .
Sharing knowledge
HandlerThread must know will be
IntentService must know will be