官方文档:
A Handler allows you to send and process Message
and Runnable objects associated with a thread's 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 post(Runnable)
,postAtTime(Runnable,long)
,postDelayed(Runnable,long)
,sendEmptyMessage(int)
,sendMessage(Message)
,sendMessageAtTime(Message,long)
,and sendMessageDelayed(Message,long)
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 Message
object containing a bundle of data that will be processed by the Handler's handleMessage(Message)
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.
对这部分文档的翻译:
post(Runnable)
,
postAtTime(Runnable, long)
,
postDelayed(Runnable, long)
,
sendEmptyMessage(int)
,
sendMessage(Message)
,
sendMessageAtTime(Message, long)
, and
sendMessageDelayed(Message, long)这七个方法。post是当获取到runnable的时候就插入到消息队列中,sendMessage方法允许我们把一个包含有数据的消息对象插入队列,而且会在Handler的handleMessage(Message)方法中执行(这个方法要在Handler的子类中实现)。
当我们向Handler中post或者send消息的时候,我们可以在消息队列准备好的时候立刻执行,也可以指定一个延迟时间对其进行处理,或者指定一个绝对时间对其进行处理。后两个允许我们实现timeouts、ticks还有其他的基于时间的行为。
当我们的应用创建一个进程时,它的主线程会专门运行一个消息队列,负责管理优先级最高的应用对象(活动、广播接收器等等)他们创建的任何窗口对象。我们也可以创建自己的线程,通过Handler与主应用线程进行通信,这个通信是和之前同样的post或者sendMessage方法完成的,只不过这次是在我们新创建的线程中。所给的Runnable或Message会被插入到消息队列中,并在适当的时候被处理。
下面是对Handler源代码的解析:
Handler类的全局变量:
- final MessageQueue mQueue;
- final Looper mLooper;
- final Callback mCallback;
- IMessenger mMessenger;
这些全局变量都会在Handler的构造函数中进行赋值:
- /**
- * 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.
- * 这是默认的构造方法,Handler是和当前线程的队列关联在一起的,如果队列不存在,那么Handler就不能接收消息。
- */
- 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.
- * 这个构造函数需要传入一个callback接口,用于处理Handler传递的Message
- */
- 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.
- *使用提供的队列来代替默认的队列,并传入一个callback接口用于处理消息
- */
- public Handler(Looper looper, Callback callback) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = callback;
- }
这里有个 FIND_POTENTIAL_LEAKS参数:找到潜在的泄露。看下注释:
- /*
- * 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.
- */
设置这个标记为true来检测不是静态的匿名,本地或成员类继承Handler类。这些类型的类可以带来潜在的泄漏。在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());
- }
- }
- /**
- * 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);
- }
当你实例化一个Handler的时候可以使用Callback接口来避免写自定义的Handler子类。这里的机制类似与Thread与runable接口的关系。
在Handler里面,子类要处理消息的话必须重写handleMessage()这个方法,因为在handler里面它是个空方法:
- /**
- * Subclasses must implement this to receive messages.
- */
- public void handleMessage(Message msg) {
- }
再来看一下:dispatchMessage()这个方法:
- /**
- * 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);
- }
- }
- private final void handleCallback(Message message) {
- message.callback.run();
- }
getMessageName()方法:
- <span style="font-size:14px;">/**
- * Returns a string representing the name of the specified message.
- * The default implementation will either return the class name of the
- * message callback if any, or the hexadecimal representation of the
- * message "what" field.
- *
- * @param message The message whose name is being queried
- */
- public String getMessageName(Message message) {
- if (message.callback != null) {
- return message.callback.getClass().getName();
- }
- return "0x" + Integer.toHexString(message.what);
- }</span>
我们从源码中结合注释,返回传入message的name值,默认的实现是如火message.callback不为空,就返回callback的类名,或者返回一个16进制的message的what值。
obtainMessage()方法:
- <span style="font-size:14px;"> /**
- * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
- * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
- * If you don't want that facility, just call Message.obtain() instead.
- */
- public final Message obtainMessage()
- {
- return Message.obtain(this);
- }</span>
从一个全局消息池里面获取一个新的Message。在Message池中检索是否存在与handler实例对应的message比创建一个新的Message更高效。如果你不想创建新Message,就是用Message.obtain方法代替。
下面是几个obtainMessage的重载方法:
- <span style="font-size:14px;"> /**
- *
- * Same as {@link #obtainMessage()}, except that it also sets the what and obj members
- * of the returned Message.
- *
- * @param what Value to assign to the returned Message.what field.
- * @param obj Value to assign to the returned Message.obj field.
- * @return A Message from the global message pool.
- */
- public final Message obtainMessage(int what, Object obj)
- {
- return Message.obtain(this, what, obj);
- }
- /**
- *
- * Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned
- * Message.
- * @param what Value to assign to the returned Message.what field.
- * @param arg1 Value to assign to the returned Message.arg1 field.
- * @param arg2 Value to assign to the returned Message.arg2 field.
- * @return A Message from the global message pool.
- */
- public final Message obtainMessage(int what, int arg1, int arg2)
- {
- return Message.obtain(this, what, arg1, arg2);
- }
- /**
- *
- * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
- * returned Message.
- * @param what Value to assign to the returned Message.what field.
- * @param arg1 Value to assign to the returned Message.arg1 field.
- * @param arg2 Value to assign to the returned Message.arg2 field.
- * @param obj Value to assign to the returned Message.obj field.
- * @return A Message from the global message pool.
- */
- public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
- {
- return Message.obtain(this, what, arg1, arg2, obj);
- }</span>
和上面相同,只是参数不同,为返回的Message的一些属性赋值。
在往下就是post()方法了:
- <span style="font-size:14px;">/**
- * Causes the Runnable r to be added to the message queue.
- * The runnable will be run on the thread to which this handler is
- * attached.
- *
- * @param r The Runnable that will be executed.
- *
- * @return Returns true if the Runnable was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }</span>
把传入的Runnable对象r加入到Message队列中,这个runnable对象将在handler关联的线程中执行。如果runnable对象被正确执行返回true,如果looper遍历消息队列时退出,则返回false。在这个方法中,主要是调用了sendMessageDelayed方法。在下面会有相应的分析。
接下来,看一下其他有关post的方法:
- <span style="font-size:14px;"> /**
- * Causes the Runnable r to be added to the message queue, to be run
- * at a specific time given by <var>uptimeMillis</var>.
- * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
- * The runnable will be run on the thread to which this handler is attached.
- *
- * @param r The Runnable that will be executed.
- * @param uptimeMillis The absolute time at which the callback should run,
- * using the {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the Runnable was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting. Note that a
- * result of true does not mean the Runnable will be processed -- if
- * the looper is quit before the delivery time of the message
- * occurs then the message will be dropped.
- */
- public final boolean postAtTime(Runnable r, long uptimeMillis)
- {
- return sendMessageAtTime(getPostMessage(r), uptimeMillis);
- }
- /**
- * Causes the Runnable r to be added to the message queue, to be run
- * at a specific time given by <var>uptimeMillis</var>.
- * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
- * The runnable will be run on the thread to which this handler is attached.
- *
- * @param r The Runnable that will be executed.
- * @param uptimeMillis The absolute time at which the callback should run,
- * using the {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the Runnable was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting. Note that a
- * result of true does not mean the Runnable will be processed -- if
- * the looper is quit before the delivery time of the message
- * occurs then the message will be dropped.
- *
- * @see android.os.SystemClock#uptimeMillis
- */
- public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
- {
- return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
- }
- /**
- * Causes the Runnable r to be added to the message queue, to be run
- * after the specified amount of time elapses.
- * The runnable will be run on the thread to which this handler
- * is attached.
- *
- * @param r The Runnable that will be executed.
- * @param delayMillis The delay (in milliseconds) until the Runnable
- * will be executed.
- *
- * @return Returns true if the Runnable was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting. Note that a
- * result of true does not mean the Runnable will be processed --
- * if the looper is quit before the delivery time of the message
- * occurs then the message will be dropped.
- */
- public final boolean postDelayed(Runnable r, long delayMillis)
- {
- return sendMessageDelayed(getPostMessage(r), delayMillis);
- }
- /**
- * Posts a message to an object that implements Runnable.
- * Causes the Runnable r to executed on the next iteration through the
- * message queue. The runnable will be run on the thread to which this
- * handler is attached.
- * <b>This method is only for use in very special circumstances -- it
- * can easily starve the message queue, cause ordering problems, or have
- * other unexpected side-effects.</b>
- *
- * @param r The Runnable that will be executed.
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean postAtFrontOfQueue(Runnable r)
- {
- return sendMessageAtFrontOfQueue(getPostMessage(r));
- }
- </span>
postAtTime 在指定时间uptimeMillis把runnable插入到队列中去,另一个postAtTime方法又加了一个Object类型的token,在下面的sendMessageAtTime中具体分析。postDelayed在延迟delayMillis时间后插入队列。postAtFrontOfQueue把Runnable插入到队首,下一次轮询就会被执行。
下面是从队列中删除对应的runable:
- <span style="font-size:14px;"> /**
- * Remove any pending posts of Runnable r that are in the message queue.
- */
- public final void removeCallbacks(Runnable r)
- {
- mQueue.removeMessages(this, r, null);
- }
- /**
- * Remove any pending posts of Runnable <var>r</var> with Object
- * <var>token</var> that are in the message queue. If <var>token</var> is null,
- * all callbacks will be removed.
- */
- public final void removeCallbacks(Runnable r, Object token)
- {
- mQueue.removeMessages(this, r, token);
- }
- </span>
下面就是重头戏SendMessage:
- <span style="font-size:14px;"> /**
- * Pushes a message onto the end of the message queue after all pending messages
- * before the current time. It will be received in {@link #handleMessage},
- * in the thread attached to this handler.
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean sendMessage(Message msg)
- {
- return sendMessageDelayed(msg, 0);
- }</span>
- <span style="font-size:14px;">/**
- * Enqueue a message into the message queue after all pending messages
- * before (current time + delayMillis). You will receive it in
- * {@link #handleMessage}, in the thread attached to this handler.
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting. Note that a
- * result of true does not mean the message will be processed -- if
- * the looper is quit before the delivery time of the message
- * occurs then the message will be dropped.
- */
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- }</span>
- <span style="font-size:14px;">/**
- * Enqueue a message into the message queue after all pending messages
- * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
- * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
- * You will receive it in {@link #handleMessage}, in the thread attached
- * to this handler.
- *
- * @param uptimeMillis The absolute time at which the message should be
- * delivered, using the
- * {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting. Note that a
- * result of true does not mean the message will be processed -- if
- * the looper is quit before the delivery time of the message
- * occurs then the message will be dropped.
- */
- public boolean sendMessageAtTime(Message msg, long uptimeMillis)
- {
- boolean sent = false;
- MessageQueue queue = mQueue;
- if (queue != null) {
- msg.target = this;
- sent = queue.enqueueMessage(msg, uptimeMillis);
- }
- else {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- }
- return sent;
- }</span>
这个方法才是真正执行插入到队列的操作,把message插入到消息队列中。
发送消息,均是调用sendMessageAtTime方法:
- <span style="font-size:14px;"> /**
- * Sends a Message containing only the what value.
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean sendEmptyMessage(int what)
- {
- return sendEmptyMessageDelayed(what, 0);
- }
- /**
- * Sends a Message containing only the what value, to be delivered
- * after the specified amount of time elapses.
- * @see #sendMessageDelayed(android.os.Message, long)
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
- Message msg = Message.obtain();
- msg.what = what;
- return sendMessageDelayed(msg, delayMillis);
- }
- /**
- * Sends a Message containing only the what value, to be delivered
- * at a specific time.
- * @see #sendMessageAtTime(android.os.Message, long)
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
- Message msg = Message.obtain();
- msg.what = what;
- return sendMessageAtTime(msg, uptimeMillis);
- }</span>
发送空消息,使用Message.obtain()方法获得一个Message(上面已经讲道这个方法)进行发送。
sendMessageAtFrotOfQueue:
- <span style="font-size:14px;"> /**
- * Enqueue a message at the front of the message queue, to be processed on
- * the next iteration of the message loop. You will receive it in
- * {@link #handleMessage}, in the thread attached to this handler.
- * <b>This method is only for use in very special circumstances -- it
- * can easily starve the message queue, cause ordering problems, or have
- * other unexpected side-effects.</b>
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean sendMessageAtFrontOfQueue(Message msg)
- {
- boolean sent = false;
- MessageQueue queue = mQueue;
- if (queue != null) {
- msg.target = this;
- sent = queue.enqueueMessage(msg, 0);
- }
- else {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- }
- return sent;
- }</span>
把一个消息插入到message queue的队首。但是我们注意到在SendMessageAtTime中插入队列代码:
- <span style="font-size:14px;">queue.enqueueMessage(msg, uptimeMillis);</span>
在uptimeMillis时间后插入到队列,而在sendMessageAtFrotOfQueue中插入队列代码:
- <span style="font-size:14px;">queue.enqueueMessage(msg, 0)</span>
按照字面意思理解,就是立即插入队列,但是立刻插入队列也不能实现插到队首。那到底是如何实现的哪?这一点,将在MessageQueue源码分析中揭晓。
从消息队列中删除对应的消息:
- <span style="font-size:14px;"> /**
- * Remove any pending posts of messages with code 'what' that are in the
- * message queue.
- */
- public final void removeMessages(int what) {
- mQueue.removeMessages(this, what, null, true);
- }
- /**
- * Remove any pending posts of messages with code 'what' and whose obj is
- * 'object' that are in the message queue. If <var>token</var> is null,
- * all messages will be removed.
- */
- public final void removeMessages(int what, Object object) {
- mQueue.removeMessages(this, what, object, true);
- }
- /**
- * Remove any pending posts of callbacks and sent messages whose
- * <var>obj</var> is <var>token</var>. If <var>token</var> is null,
- * all callbacks and messages will be removed.
- */
- public final void removeCallbacksAndMessages(Object token) {
- mQueue.removeCallbacksAndMessages(this, token);
- }</span>
检查消息队列中是否存在相对应的消息:
- <span style="font-size:14px;"> /**
- * Check if there are any pending posts of messages with code 'what' in
- * the message queue.
- */
- public final boolean hasMessages(int what) {
- return mQueue.removeMessages(this, what, null, false);
- }
- /**
- * Check if there are any pending posts of messages with code 'what' and
- * whose obj is 'object' in the message queue.
- */
- public final boolean hasMessages(int what, Object object) {
- return mQueue.removeMessages(this, what, object, false);
- }</span>
获取当前looper:
- <span style="font-size:14px;">public final Looper getLooper() {
- return mLooper;
- }</span>
往下,dump方法,从字面意思上理解:转储,具体作用,还不太了解,将在Looper源码解析中分析下。
- <span style="font-size:14px;"> public final void dump(Printer pw, String prefix) {
- pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
- if (mLooper == null) {
- pw.println(prefix + "looper uninitialized");
- } else {
- mLooper.dump(pw, prefix + " ");
- }
- }</span>
获取当前Messenger:
- <span style="font-size:14px;">final IMessenger getIMessenger() {
- synchronized (mQueue) {
- if (mMessenger != null) {
- return mMessenger;
- }
- mMessenger = new MessengerImpl();
- return mMessenger;
- }
- }</span>
关于Messenger信使类,请关注以后源码分析。
将一个Runnable封装成一个Message。
- <span style="font-size:14px;"> private final Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
- private final Message getPostMessage(Runnable r, Object token) {
- Message m = Message.obtain();
- m.obj = token;
- m.callback = r;
- return m;
- }</span>
getPostMessage这个方法在上面所说的post系列方法中,被广泛使用。
处理message里面的runnable消息,直接调用了run方法。
- <span style="font-size:14px;">private final void handleCallback(Message message) {
- message.callback.run();
- }</span>