android的消息机制概述
1.android的消息机制主要是指handler的运行机制,Handler的运行需要底层MessageQueue,Looper的支持。
MessageQueue:消息队列,note:虽然它的中文翻译是消息队列,但是注意它的内部存储结构并不是队列,而是采用单链表的形式存储消息。
Looper:消息循环。消息到来后如何对消息进行处理呢,此时就要用到Looper了,Looper会开启一个无限循环,源源不断的对MessageQueue中的消息进行处理,直到全部处理完后,Looper会处于挂起状态,等待下个消息的到来。
Handler源码解析
Handler handler = new Handler();
public Handler(@Nullable Callback callback, boolean async) {
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;
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
构造handler的时候首先会检查当前线程是否有Looper对象的存在,如果Looper不存在,则会抛出异常。主线程的Looper会在activity启动的时候就已创建,可以放心使用。所以我们如果想要在子线程使用handler消息机制,必须遵循以下格式:
new Thread(() -> {
// 构建looper
Looper.prepare();
Handler handler = new Handler();
// 开启循环
Looper.loop();
}).start();
说到Looper的时候需要看一下ThreadLocal。Handler是如何获取当前线程的Looper呢?可以看到源码中直接使用了sThreadLocal.get();
ThreadLocal的作用是可以保证在多线程中互不干扰的存储并使用数据。
Handler发送消息
handler创建完毕后就可以使用发送消息了,最终都会走到如下方法:
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//这里会将target设置为当前对象。
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,调用handler.sendMessage(msg)后,最终其实只是将该消息放入消息队列中。
如下是消息入队过程,可以很明显的看出消息在消息队列中是以链表的形式存在的:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
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;
}
MessageQueue.java
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
}
}
可以发现next()是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。有消息到来时,next方法会返回这条消息,并将这条消息从单链表中移除。
至此消息已全部放入到消息队列中,接下来我们一起看一下是如何对消息进行处理的。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
me.mInLoop = true;
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();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);//target对象在上面会设置为this,因此会调用当前对象的dispatchMessage方法
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
使用looper.loop开启循环后,会从消息队列中取出消息,next()方法可能会阻塞。如果有消息取出,则会调用msg.target.dispatchMessage(msg)进行消息处理。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到dispatchMessage是有顺序的。首先处理message中的callback,如果不存在则处理当前handler中的callback。都不存在才会处理子类中的handleMessage方法。
handleMessage是一个空方法,需要子类实现。
public void handleMessage(@NonNull Message msg) { }