Handler解析(二):消息post与sendMessage机制

来源:互联网 发布:安装ubuntu后无法上网 编辑:程序博客网 时间:2024/06/05 17:33

       上一篇Handler解析(一):是如何实现线程之间的切换分析了Handler线程切换的原理,本篇主要介绍Handler的post机制。

Post与sendMessage方法


首先看看Handler中包含的Post和sendMessage相关的方法

boolean post(Runnable r)boolean postAtTime(Runnable r, long uptimeMillis)boolean postAtTime(Runnable r, Object token, long uptimeMillis)boolean postDelayed(Runnable r, long delayMillis)boolean postAtFrontOfQueue(Runnable r)boolean sendMessage(Message msg)boolean sendMessageDelayed(Message msg, long delayMillis)boolean sendMessageAtTime(Message msg, long uptimeMillis)boolean sendMessageAtFrontOfQueue(Message msg)

查看Handler的源码,下面通过源码一一对它们进行分析,看它们的实现原理是怎样的。

post(Runnable r)与sendMessage()


源码分析

 public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }

post方法通过调用getPostMessage()生成一个message,然后调用sendMessageDelayed()

 private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;}   public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }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);    }

getPostMessage()函数中调用Message.obtain()获取一个消息,并将post的runnable参数赋值给了message的callback。Message.obtain()是从消息池中获取一个message,实现message的复用,减少message的创建以及回收。

sendMessageDelayed()函数,其中调用了sendMessageAtTime(),其中的uptimeMillis为SystemClock.uptimeMillis() + delayMillis,即当前的时间加上delayMillis,绝对时间。而Post方法中传入的delayMillis为0,表示立即处理。

sendMessageAtTime()函数,将生成的message发送出去,其中的参数uptimeMillis表示绝对时间,通过post()函数调用的uptimeMillis为当前时间。在sendMessageAtTime函数中调用了enqueueMessage()函数,将消息加入队列,其中的queue参数即handler创建时所创建的mQueue队列,查看上一篇文章Handler解析(一):是如何实现线程之间的切换了解详情。

MessageQueue enqueueMessage()函数


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

enqueueMessage()函数,其中msg.target = this;这行代码,message的target引用了当前的handler。然后调用queue.enqueueMessage(),重点来了…

boolean enqueueMessage(Message msg, long when) {     //当前message是否设置了target,从上面分析可以知道target为当前handler        if (msg.target == null) {exception}        //当前message是否被标记为use,        if (msg.isInUse()) { exception }        synchronized (this) {        //是否准备退出,若准备退出就回收消息            if (mQuitting) {                msg.recycle();                return false;            }            //将当前message标记为use            msg.markInUse();            //将sendMessageAtTime传入的时赋值到when            msg.when = when;            //当前队列最前面的message,            Message p = mMessages;            boolean needWake;            //p == null,表示当前队列为空            //when == 0,表示当前消息为即时处理            //when < p.when,表示当前加入的消息的处理时间早于当前队列最前的消息的时间            //此三种情况下,将当前消息放到队列的最前端            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                //当前looper线程是否处于阻塞状态,阻塞状态则需要唤醒线程                needWake = mBlocked;            } else {                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                //此处一个for无限循环比较处理时间                for (;;) {                    //消息队列下一个消息                    prev = p;                    p = p.next;                    //消息队列为null或者下一个消息的处理时间晚于当前消息处理的时候,退出循环比较                    if (p == null || when < p.when) {                        break;                    }                    //handler发送的消息p.isAsynchronous()为false                    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.            //当looper线程需要唤醒的时候则唤醒,从消息队列中取出消息进行处理            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }

上面的代码逻辑:
①首先进行一些基本的判断,message是否正在被使用(是否已经插入到了队列),插入到了队列则直接返回;当前looper是否处于准备退出状态,准备退出则直接回收message;
②开始比较message中的when,从上面的分析可以得知,message的when为sendMessageAtTime中的uptimeMillis,即message处理的绝对时间。通过比较将当前的message插入到队列中去。其中when是从小到大进行排列。典型的冒泡排序算法。
③若looper线程处于阻塞状态,则需要唤醒线程来处理message

Looper loop()函数


从上面的分析可以知道,当message插入到消息队列之后,会通过nativeWake唤醒looper线程来处理消息,looper线程从消息队列中取出message进行处理

 public static void loop() {        final Looper me = myLooper();        final MessageQueue queue = me.mQueue;        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        //死循环取消息        for (;;) {             //调用MessageQueue的next取出下一个该处理的消息            Message msg = queue.next(); // might block            //当消息为null时,退出loop            if (msg == null) {                return;            }             //取出消息之后,交给target即handler的dispatchMessage进行处理            msg.target.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // 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.recycleUnchecked();        }    }

Looper的loop()函数实现很简单,通过一个死循环从MessageQueue中取出message,若当前没有message则阻塞,否则将取回的message交给handler进行处理

MessageQueue next()函数


 Message next() {        int pendingIdleHandlerCount = -1; // -1 only during first iteration        //下一个消息需要处理的绝对时间        int nextPollTimeoutMillis = 0;        //死循环        for (;;) {            //阻塞线程到下一个消息的处理时间            nativePollOnce(ptr, nextPollTimeoutMillis);            synchronized (this) {            //获取当前时间               final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                //获取消息队列第一个消息,即下一个要处理的消息                Message msg = mMessages;                //通过handler发送的message,target非null                if (msg != null && msg.target == null) {                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    //比较当前时间与下一个消息要处理的绝对时间                    //如果下一个消息处理的绝对时间大于当前时间                    //获取下一个消息距离当前时间的相对时间                    if (now < msg.when) {                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // 有即将需要被处理的消息                        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;                }                //若要退出,则返回为null,looper的loop函数取出消息为null时,会退出loop()函数                if (mQuitting) {                    dispose();                    return null;                }        }    }

MessageQueue的next()函数,将队列的第一个message的when(message处理的绝对时间)和当前时间做比较,若早于当前时间,则表示message需要立即被处理,将消息队列的第一个message取出,交给Looper的loop()函数进行处理,否则调用nativePollOnce()函数阻塞到队列中第一个message需要处理的时间。这里可以得知为什么需要在enqueueMessage()的时候调用nativeWake()唤醒线程,因为当有message进入队列的时候,若该message的when早于当前消息队列的第一个message的when时,需要重置线程的阻塞时间。

Handler dispatchMessage()函数


在looper的loop()函数中,会将从消息队列中取出来的message交给dispatchMessage来进行处理

   public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }   /**     * Subclasses must implement this to receive messages.     */    public void handleMessage(Message msg) {    }private static void handleCallback(Message message) {        message.callback.run();    }

通过上面的分析可以知道,
1.post()函数通过getPostMessage()中获取message,其中将runnable赋值给了message的callback,通过上面的dispatchMessage()函数,会先判断message的callback不为空时,直接执行callback,即post中的runnable的run方法。
2.sendMessage方法中的message没有自动设置callback,此时会判断mCallback是不是为null,若为null,则执行handleMessage(),而handleMessage()没有任何实现,也就是我们在定义handler的时候需要重写handleMessage()来处理消息。

总结


消息post与sendMessage的基本流程如下,
Handler.post() –> handler.sendMessage() –> MessageQueue.enqueueMessage –> Looper.loop() –> MessageQueue.next() –> Handler.dispatchMessage()

post()是直接调用sendMessage,区别在于post()的参数为runnable,而sendMessage()的参数为message,post()的runnable通过getMessage()函数进行包装;在handleMessage()处理消息时,post()的会调用runnable执行run(),而sendMessage的会交给handler的handleMessage()来处理。

阅读全文
0 0
原创粉丝点击