请先参阅 Handler发送消息和延时Message实现原理。
为什么没有先讲Looper和MessageQueue是因为想从使用的角度来分析整个流程,对于开发者来说,第一个使用的其实是Handler。
前边我们分析过,Handler在sendMessage时,Message会有一个target指向当前的Handler,并且Message是一个单链表结构。我们来一起看下Message的源码。
/**
*
* Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}. This object contains two extra int fields and an
* extra object field that allow you to not do allocations in many cases.
*
* While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.
*/
public final class Message implements Parcelable {
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
/**
* Optional Messenger where replies to this message can be sent. The
* semantics of exactly how this is used are up to the sender and
* receiver.
*/
public Messenger replyTo;
/** If set message is in use */
/*package*/ static final int FLAG_IN_USE = 1;
/** Flags reserved for future use (All are reserved for now) */
/*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE;
/** Flags to clear in the copyFrom method */
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE;
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 10;
}
可以看到Message有一个名为next,类型为Message的field,这个说明了Message本身是一个单链表的结构。what表示消息的类型,when的作用前边已经分析过了,obj我们一直在使用。另外需要额外注意的是名为target类型为Handler的field,还有名为callback类型为Runnable的field。现在只需要知道我们使用Handler发送的都是Message对象,Message对象身上有一个指向对应Handler的target属性。其它的例如obtainMessage和Messenger请看后续分解。
Handler发送的Message最终是通过MessageQueue的enqueueMessage来加入消息队列的。所以第一个问题是Handler里边的mQueue是怎么来的。让我们看下Handler的源码。
/**
* A Handler allows you to send and process {@link Message} and Runnable
* objects associated with a thread's {@link MessageQueue}. Each Handler
* instance is associated with a single thread and that thread's message
* queue. When you create a new Handler, it is bound to the thread /
* message queue of the thread that is creating it -- from that point on,
* it will deliver messages and runnables to that message queue and execute
* them as they come out of the message queue.
*
* There are two main uses for a Handler: (1) to schedule messages and
* runnables to be executed as some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.
*
*
Scheduling messages is accomplished with the
* {@link #post}, {@link #postAtTime(Runnable, long)},
* {@link #postDelayed}, {@link #sendEmptyMessage},
* {@link #sendMessage}, {@link #sendMessageAtTime}, and
* {@link #sendMessageDelayed} methods. The post versions allow
* you to enqueue Runnable objects to be called by the message queue when
* they are received; the sendMessage versions allow you to enqueue
* a {@link Message} object containing a bundle of data that will be
* processed by the Handler's {@link #handleMessage} method (requiring that
* you implement a subclass of Handler).
*
*
When posting or sending to a Handler, you can either
* allow the item to be processed as soon as the message queue is ready
* to do so, or specify a delay before it gets processed or absolute time for
* it to be processed. The latter two allow you to implement timeouts,
* ticks, and other timing-based behavior.
*
*
When a
* process is created for your application, its main thread is dedicated to
* running a message queue that takes care of managing the top-level
* application objects (activities, broadcast receivers, etc) and any windows
* they create. You can create your own threads, and communicate back with
* the main application thread through a Handler. This is done by calling
* the same post or sendMessage methods as before, but from
* your new thread. The given Runnable or Message will then be scheduled
* in the Handler's message queue and processed when appropriate.
*/
public class Handler {
/*
* Set this flag to true to detect anonymous, local or member classes
* that extend this Handler class and that are not static. These kind
* of classes can potentially create leaks.
*/
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Handler";
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
/**
* Constructor associates this handler with the queue for the
* current thread and takes a callback interface in which you can handle
* messages.
*/
public Handler(Callback callback) {
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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
}
/**
* Use the provided queue instead of the default one.
*/
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
/**
* Use the provided queue instead of the default one and take a callback
* interface in which to handle messages.
*/
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
IMessenger mMessenger;
}
我们重点关注Handler的三个field,分别是mQueue,mLooper,mCallback。通过查看Handler的构造方法,我们可以清楚的看到,其实主要就是为这个三成员变量来赋值的。其中当没有looper的构造中,会把当前线程中的Looper赋值给mLooper,然后检查mLooper时可能会报我们最常见的关于Handler的错误"Can't create handler inside thread that has not called Looper.prepare()",完成mLooper赋值后,直接把mLooper的mQueue赋值给Handler的mQueue。
到此为止,我们已经明白了Message的产生和结构,然后Handler把Message通过MessageQueue 的enqueueMessage加入到消息队列。剩下的问题就是消息队列是如何把消息取出并执行的。
这就需要分析Looper的源码了。
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* Most interaction with a message loop is through the
* {@link Handler} class.
*
*
This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
*
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }
*/
public class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
final MessageQueue mQueue;
final Thread mThread;
volatile boolean mRun;
private Printer mLogging = null;
private static Looper mMainLooper = null; // guarded by Looper.class
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
/** Returns the application's main looper, which lives in the main thread of the application.
*/
public synchronized static Looper getMainLooper() {
return mMainLooper;
}
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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();
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
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.recycle();
}
}
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
/**
* Control logging of messages as they are processed by this Looper. If
* enabled, a log message will be written to printer
* at the beginning and ending of each message dispatch, identifying the
* target Handler and message contents.
*
* @param printer A Printer object that will receive log messages, or
* null to disable message logging.
*/
public void setMessageLogging(Printer printer) {
mLogging = printer;
}
/**
* Return the {@link MessageQueue} object associated with the current
* thread. This must be called from a thread running a Looper, or a
* NullPointerException will be thrown.
*/
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
public void quit() {
Message msg = Message.obtain();
// NOTE: By enqueueing directly into the message queue, the
// message is left with a null target. This is how we know it is
// a quit message.
mQueue.enqueueMessage(msg, 0);
}
/**
* Return the Thread associated with this Looper.
*/
public Thread getThread() {
return mThread;
}
/** @hide */
public MessageQueue getQueue() {
return mQueue;
}
}
Looper的重要的field分别是:
final MessageQueue mQueue;
final Thread mThread;
重要的方法是:
prepare和loop。
构造方法当然重要了。在唯一的构造方法中,new了一个MessageQueue赋值给mQueue,然后把当前线程赋值给mThread,完成对两个重要的field赋值。但是不要想当然,构造方法是private的,我们是无法直接调用的。答案就在另外一个重要的方法里边,prepare。
查看prepare的源码我们可以清楚的看到, sThreadLocal.set(new Looper());确实new了一个Looper对象存储到sThreadLocal中,关于ThreadLocal后边有机会了再详细分析,大家可以简单的理解为它是一个key相同,但value在各个线程不同的存储空间。这样我们就明白了,prepare不能调用多次,那大家有没有想过为什么呢。个人猜测是这样的,如果第二次调用prepare就会再次调用构造方法,然后mQueue的值就发生改变了,而我们知道,消息队列里边是有延时消息的,也就是还没有消费的消息,如果直接修改mQueue就可能造成消息的丢失。
查看loop的源码,我们可以看到,最重要的是一个while(true)的循环。里边通过MessageQueue的next方法取得一个Message,然后调用Message的target即对应Handler的dispatch方法来处理消息,然后直接recycle就结束了。很简单是吧,里边的核心是MessageQueue的next方法和Handler的dispatch方法。我们先来分析dispatch。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
前边我们分析过,当我们使用handler的post方法时,我们post的Runnable对象被赋值到msg给callback,这就是第一种情况,调用的是handleCallback(msg)。
private final void handleCallback(Message message) {
message.callback.run();
}
直接调用我们的Runnable对象的run方法,结束了这个消息的整个调用(所以我们post的那些代码是这个时候才执行的)。
当我们是使用Handler的send方法来发送Message时,首先检查mCallback是否为空,这个mCallback都是通过构造进行赋值的,这个就不再分析了。当mCallback为空时,调用的是我们最熟悉的handleMessage方法,就是我们平时需要重写的Handler的最重要的方法,在这也不再分析了。
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount == 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
后边的大部分代码都是处理刚开始的初始化情况。当开始正常运行后,重要的是前半段,取出一个mMessages,然后比较时间看看是否需要执行,如果需要执行,会让线程停止阻塞,然后把mMessages指向下一个,把已经取出的msg标记成使用中然后返回给Looper。如果还不需要执行,会算出一个时间差,然后下一个循环会调用nativePollOnce,猜测是一个阻塞等待的方法,跟enqueueMessage的nativeWake功能相反。
现在我们来总结一下,每个线程只会调用一次Looper.prepare(),也就决定了每个线程只会有一个Looper和一个MessageQueue,我们在不同的Activity中都可以new Handler,所以Handler可以是多个的。我们在子线程中new Handler会报错,但是在UI线程却没有问题,原因是因为UI线程已经调用过Looper.prepare()和Looper.loop()。我们可以查看代码是在ActivityThread的main方法中调用的。有人可能会有疑问loop之后线程就是阻塞式的,存在一个死循环,那我们的UI线程是怎么处理的呢,我们的Activity等是怎么切换的呢。答案很简单,所有的操作都是通过Message机制来实现的,只不过系统不需要new Handler,它的操作更直接,有兴趣的可以查看Instrumentation类的源码。