移步Android Handler机制详解

  • 1、Handler发送消息
  • 2、Handler的send方案
  • 3、Handler的post方案

1 Handler发送消息

send方案.png
post方案.png
  • send方案发送消息(需要回调才能接收消息)
  1. sendMessage(Message) 立即发送Message到消息队列
  2. sendMessageAtFrontOfQueue(Message) 立即发送Message到队列,而且是放在队列的最前面
  3. sendMessageAtTime(Message,long) 设置时间,发送Message到队列
  4. sendMessageDelayed(Message,long) 延时若干毫秒后,发送Message到队列
  • post方案 立即发送Message到消息队列
  1. post(Runnable) 立即发送Message到消息队列
  2. postAtFrontOfQueue(Runnable) 立即发送Message到队列,而且是放在队列的最前面
  3. postAtTime(Runnable,long) 设置时间,发送Message到队列
  4. postDelayed(Runnable,long) 在延时若干毫秒后,发送Message到队列

2 Handler的send方案

以Handler的sendMessage(Message msg)为例子

2.1 sendMessage(Message msg)方法

代码在Handler.java 505行

/**     * 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);    }
  • 在当前时间,在所有待处理消息之后,将消息推送到消息队列的末尾。在和当前线程关联的的Handler里面的handleMessage将收到这条消息

2.1.1 boolean sendMessageDelayed(Message msg, long delayMillis)

代码在Handler.java 565行

/**     * 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);    }

该方法内部就做了两件事

  • 1、判断delayMillis是否小于0
  • 2、调用了public boolean sendMessageAtTime(Message msg, long uptimeMillis)方法

2.1.2 boolean sendMessageAtTime(Message msg, long uptimeMillis)

代码在Handler.java 592行

/**     * Enqueue a message into the message queue after all pending messages     * before the absolute time (in milliseconds) uptimeMillis.     * The time-base is {@link android.os.SystemClock#uptimeMillis}.     * Time spent in deep sleep will add an additional delay to execution.     * 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) {        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);    }
  • 以android系统的SystemClock的uptimeMillis()为基准,以毫秒为基本单位的绝对时间下,在所有待处理消息后,将消息放到消息队列中。
  • 深度睡眠中的时间将会延迟执行的时间,你将在和当前线程办的规定的Handler中的handleMessage中收到该消息。
  • 因为通常我们理解的异步是指新开一个线程,但是这里不是,因为异步的也是发送到looper所绑定的消息队列中,这里的异步主要是针对Message中的障栅(Barrier)而言的,当出现障栅(Barrier)的时候,同步的会被阻塞,而异步的则不会。所以这个异步仅仅是一个标记而已。

该方法内部就做了两件事

  • 1、获取消息队列,并对该消息队列做非空判断,如果为null,直接返回false,表示消息发送失败
  • 2、调用了boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法

2.1.3 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

代码在Handler.java 626行

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

本方法内部做了三件事

    1. 设置msg的target变量,并将target指向自己
    1. 如果Handler的mAsynchronous值为true(默认为false,即不设置),则设置msg的flags值,让是否异步在Handler和Message达成统一。
    1. 调用MessageQueue的enqueueMessage()方法

2.1.4 boolean enqueueMessage(Message msg, long when)方法

代码在MessageQueue.java 533行

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;             // 第六步            //根据when的比较来判断要添加的Message是否应该放在队列头部,当第一个添加消息的时候,            // 测试队列为空,所以该Message也应该位于头部。            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                // 把msg的下一个元素设置为p                msg.next = p;                // 把msg设置为链表的头部元素                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.                //除非消息队列的头部是障栅(barrier),或者消息队列的第一个消息是异步消息,                //否则如果是插入到中间位置,我们通常不唤醒消息队列,                 // 第八步                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                  // 第九步                 // 不断遍历消息队列,根据when的比较找到合适的插入Message的位置。                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;    }
  • 第1步骤、 判断msg的target变量是否为null,如果为null,则为障栅(barrier),而障栅(barrier)入队则是通过postSyncBarrier()方法入队,所以msg的target一定有值
  • 第2步骤、 判断msg的标志位,因为此时的msg应该是要入队,意味着msg的标志位应该显示还未被使用。如果显示已使用,明显有问题,直接抛异常。
  • 第3步骤、 加入同步锁。
  • 第4步骤、 判断消息队列是否正在被关闭,如果是正在被关闭,则return false告诉消息入队是失败,并且回收消息
  • 第5步骤、 设置msg的when并且修改msg的标志位,msg标志位显示为已使用
  • 第6步骤、 如果p==null则说明消息队列中的链表的头部元素为null;when == 0 表示立即执行;when< p.when 表示 msg的执行时间早与链表中的头部元素的时间,所以上面三个条件,那个条件成立,都要把msg设置成消息队列中链表的头部是元素
  • 第7步骤、 如果上面三个条件都不满足则说明要把msg插入到中间的位置,不需要插入到头部
  • 第8步骤、 如果头部元素不是障栅(barrier)或者异步消息,而且还是插入中间的位置,我们是不唤醒消息队列的。
  • 第9步骤、 进入一个死循环,将p的值赋值给prev,前面的带我们知道,p指向的是mMessage,所以这里是将prev指向了mMessage,在下一次循环的时候,prev则指向了第一个message,一次类推。接着讲p指向了p.next也就是mMessage.next,也就是消息队列链表中的第二个元素。这一步骤实现了消息指针的移动,此时p表示的消息队列中第二个元素。
  • 第10步骤、 p==null,则说明没有下一个元素,即消息队列到头了,跳出循环;p!=null&&when < p.when 则说明当前需要入队的这个message的执行时间是小于队列中这个任务的执行时间的,也就是说这个需要入队的message需要比队列中这个message先执行,则说明这个位置刚刚是适合这个message的,所以跳出循环。 如果上面的两个条件都不满足,则说明这个位置还不是放置这个需要入队的message,则继续跟链表中后面的元素,也就是继续跟消息队列中的下一个消息进行对比,直到满足条件或者到达队列的末尾。
  • 第11步骤、 因为没有满足条件,说明队列中还有消息,不需要唤醒。
  • 第12步骤、 跳出循环后主要做了两件事:事件A,将入队的这个消息的next指向循环中获取到的应该排在这个消息之后message。事件B,将msg前面的message.next指向了msg。这样就将一个message完成了入队。
  • 第13步骤、 如果需要唤醒,则唤醒,具体请看后面的Handler中的Native详解。
  • 第14步骤、 返回true,告知入队成功。

插入消息后,有条件地执行nativeWake去唤醒epoll。needWake的值依赖mBlocked——当进入next()方法时没有待处理的消息,mBlock为true,有消息并返回到looper时,mBlock为false。

结合next()和enqueueMessage()方法,得知nativeWake被调用的条件为:

  1. next()方法在等待新消息,且新插入消息的为链表头时。needWake为true
  2. 设置了Sync Barrier,且插入的消息是“异步”的。needWake为true

2.2 boolean sendMessageAtFrontOfQueue(Message msg)

代码在Handler.java 615行

/**     * 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.     * 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.     *       * @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) {        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, 0);    }
  • 在消息队列的最前面插入一个消息,在消息循环的下一次迭代中进行处理。
  • 由于它可以轻松的解决消息队列的排序问题和其他的意外副作用。

方法内部的实现和boolean sendMessageAtTime(Message msg, long uptimeMillis)大体上一致,唯一的区别就是该方法在调用enqueueMessage(MessageQueue, Message, long)方法的时候,最后一个参数是0而已。

2.3 boolean sendEmptyMessage(int what)

代码在Handler.java 517行

/**     * 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);    }

发送一个仅有what的Message
boolean sendEmptyMessageDelayed(int what, long delayMillis)
代码在Handler.java 531行

/**     * 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);    }
  • 发送一个仅有what的Message,并且延迟特定的时间发送
  • 这个方法内部主要就是做了3件事

1、调用Message.obtain();从消息对象池中获取一个空的Message。
2、设置这个Message的what值
3、调用sendMessageDelayed(Message,long) 将这个消息方法

2.4 boolean sendEmptyMessageAtTime(int what, long uptimeMillis)

代码在Handler.java 547行

/**     * 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);    }
  • 发送一个仅有what的Message,并且在特定的时间发送
  • 这个方法内部主要就是做了3件事
  • 1、调用Message.obtain();从消息对象池中获取一个空的Message。
  • 2、设置这个Message的what值
  • 3、调用sendMessageAtTime(Message,long) 将这个消息方法

2.5 小结

这些send方案都会从这里或者那里最终走到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)


5713484-e56c82c4e36768f5.png

3 Handler的post方案

3.1 boolean post(Runnable r)方法

代码在Handler.java 324行

/**     * 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);    }
  • 将一个Runnable添加到消息队列中,这个runnable将会在和当前Handler关联的线程中被执行。

  • 就是调用了sendMessageDelayed(Message, long);这个方法,所以可见boolean post(Runnable r)这个方法最终还是走到上面说到的send的流程中

  • 最终调用boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)。

3.1.1 Message getPostMessage(Runnable r)方法

代码在Handler.java 725行

    private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

代码很简单,主要是做了两件事

  • 通过Message.obtain()从消息对象池中获取一个空的Message
  • 将这空的Message的callback变量指向Runnable

最后返回这个Message m。

3.1.2 小结

  • boolean post(Runnable r)方法的内置也是通过Message.obtain()来获取一个Message对象m,然后仅仅把m的callback指向参数r而已。
  • 最后最终通过调用send方案的某个流程最终调用到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

3.2 boolean postAtTime(Runnable r, long uptimeMillis)方法

代码在Handler.java 347行

/**     * Causes the Runnable r to be added to the message queue, to be run     * at a specific time given by uptimeMillis.     * The time-base is {@link android.os.SystemClock#uptimeMillis}.     * Time spent in deep sleep will add an additional delay to execution.     * 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);    }
  • 将一个Runnable添加到消息队列中,这个runnable将会一个特定的时间被执行,这个时间是以android.os.SystemClock.uptimeMillis()为基准。
  • 如果在深度睡眠下,会推迟执行的时间,这个Runnable将会在和当前Hander关联的线程中被执行。
  • 方法内部也是先是调用getPostMessage(Runnable)来获取一个Message,这个Message的callback字段指向了这个Runnable,然后调用sendMessageAtTime(Message,long)。

3.3 boolean postAtTime(Runnable r, Object token, long uptimeMillis)方法

代码在Handler.java 372行

/**     * Causes the Runnable r to be added to the message queue, to be run     * at a specific time given by uptimeMillis.     * The time-base is {@link android.os.SystemClock#uptimeMillis}.     * Time spent in deep sleep will add an additional delay to execution.     * 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);    }
  • 这个方法和上个方法唯一不同就是多了Object参数,而这个参数仅仅是把Message.obtain();获取的Message的obj字段的指向第二个入参token而已。最后也是调用sendMessageAtTime(Message,long)。

2.4 boolean postDelayed(Runnable r, long delayMillis)方法

代码在Handler.java 396行

/**     * 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.     * The time-base is {@link android.os.SystemClock#uptimeMillis}.     * Time spent in deep sleep will add an additional delay to execution.     *       * @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);    }

这个方法也很简单,就是依旧是通过getPostMessage(Runnable)来获取一个Message,最后调用sendMessageDelayed(Message,long)而已。

3.5 boolean postAtFrontOfQueue(Runnable r)方法

代码在Handler.java 416行

/**     * 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.     * 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.     *       * @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));    }

就是依旧是通过getPostMessage(Runnable)来获取一个Message,最后调用sendMessageAtFrontOfQueue(Message)而已。

3.6 小结

  • Handler的post方案:都是通过Message getPostMessage(Runnable )中调用Message m = Message.obtain();来获取一个空的Message,然后把这个Message的callback变量指向了Runnable,最终调用相应的send方案而已。

所以我们可以这样说:
Handler的发送消息(障栅除外),无论是通过send方案还是pos方案最终都会做走到 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 中

5713484-5d5c06c67b925a3d.png

参考

Android Handler机制7之消息发送

更多相关文章

  1. android onNewIntent
  2. [置顶] android 第三方jar库 反射得到自己的资源ID
  3. TextView设置autoLink
  4. Android游戏开发之旅三 View类详解
  5. Android初级教程_获取Android控件的宽和高 .
  6. 初涉android中的回调机制
  7. Android(安卓)如何通过menu id来得到menu item 控件
  8. Android(安卓)4.4以上 根据uri获取路径的方法
  9. Android中的Sqlite数据库操作总结

随机推荐

  1. Android应用程序启动过程源代码分析(4)
  2. android 返回 弹出对话框 确认退出
  3. Android实现左右滑动页面
  4. android socket通信 创建线程接受 客户端
  5. android 图片倒影处理
  6. Android仿IOS UIAlertView对话框
  7. Android单元测试与日志输出
  8. Android(安卓)读取本地(SD卡)图片
  9. android 3.2 bug in apn setting
  10. LinearLayout 特有属性