Android开发经验谈Android技术知识Android开发

Android Handler源码之消息发送

2020-09-01  本文已影响0人  Jackson杰

消息发送

创建消息

创建消息使用下面的代码:

Message msg = Message.obtain(); // 实例化消息对象
msg.what = 1; // 消息标识
msg.obj = "AA"; // 消息内容存放
handler.sendMessage(msg);

创建消息,获取Message对象,最好的方法是调用Message.obtain(),而不是直接通过new Message()的方式,因为Message.obtain()是从一个可回收的对象池中获取Message对象,避免了重复创建。

关于Message的源码分析可以看下这篇文章:
Messgae源码分析

发送消息

查看一下Handler的类结构图:




Handler发送消息主要有sendMessage和post两种方案,

  1. send方案发送消息
  1. post方案

下面先从send方案中的第一个sendMessage() 开始源码跟踪。

send方式

sendMessage()

发送消息在执行了handler.sendMessage(msg)之后,我们往下追踪源码。
调用了下面的方法:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

继续调用了sendMessageDelayed(msg, 0)方法:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

发现继续调用了sendMessageAtTime()方法,继续往下看:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
 // 1
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // 2
    return enqueueMessage(queue, msg, uptimeMillis);
}

这个方法主要做了两件事,

enqueueMessage()

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

主要做了三件事:

public Handler() {
    this(null, false);
}

在下面Handler的构造方法里,将mAsynchronous设置为false。

public Handler(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;
}

3、调用MessageQueue的enqueueMessage()方法。

继续看enqueueMessage()方法。

boolean enqueueMessage(Message msg, long when) {

        // 1 判断msg的target变量是否为null,如果为null则抛出异常
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    
    // 2 判断msg的标志位,此时msg应该是要入队,msg的标志位应该是还未被使用,如果是已使用状态,很明显是有问题的,直接抛出异常
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    
        // 3 加入同步锁
    synchronized (this) {
    
    // 4 判断消息队列是否是关闭状态,如果是关闭状态,return false消息入队失败,并且回收消息
        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;
        }
        
                // 5 设置msg的when,修改msg的标志位为已使用状态
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        // 6 主要是用来判断要入队的msg是否位于队列头部。第一个msg时,mMessages为null,所以该msg应该位于头部
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // 把msg的下一个元素设置为p,p是mMessages,mMessages此
            //时为null,所以第一个message的next指向了null
            msg.next = p;
            // 把msg设置为链表的头部元素
            mMessages = msg;
            // needWake需要根据mBlocked的情况考虑是否触发,如果有阻
            // 塞,则需要唤醒,让它立刻去拿消息处理
            needWake = mBlocked;
        } else {  // 7 如果上面三个条件都不满足则说明要把msg插入到中间的位置
        // 8 ,如果头部元素不是障栅(barrier)或者异步消息,而且还是插入
        // 中间的位置,我们是不唤醒消息队列的
        needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 9 不断遍历消息队列,根据when的比较找到合适的插入
            // Message的位置
            for (;;) {
                    // 设置前一个message指针
                prev = p;
                // 设置下一个message指针
                p = p.next;
                // 10
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 11
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        // 12
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

整个流程可以用下面的图片表示:


第一个message入队
第二个message入队
message入队

总结:遍历消息队列中的所有消息,根据when的比较,找到合适的message的入队位置。

sendMessageAtFrontOfQueue()

查看源码:

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

和sendMessageAtTime()大体上一样,唯一的区别是该方法在调用enqueueMessage()方法时,最后一个参数是0。
查看MessageQueue的enqueueMessage()方法发现,当when==0时,会一直走if语句的内容,msg会一直插在消息队列的头部。

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{
    ......省略代码
}

sendEmptyMessage

sendEmptyMessage()方法,调用了sendEmptyMessageDelayed()方法

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

sendEmptyMessageDelayed()方法,内阻组装了一个仅有what的Message,然后调用sendMessageDelayed(),从这不开始就跟sendMessage()往后的流程相同。

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

sendEmptyMessageAtTime

查看源码,直接调用了sendEmptyMessageAtTime()。

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

以上就是发送消息的send方案,最终都会走向了enqueueMessage()方法。


post方式

查看post方式的源码,可以看到,在方法内部,还是调用了sendMessageDelayed()方法,最终也会走到上面的send流程,最终调用enqueueMessage()方法。这其中有个getPostMessage()方法。

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

查看getPostMessage()的源码

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

这个方法主要做了两件事:

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

总结

Handler的发送消息,无论是通过send方案还是pos方案最终都会做走到 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)中。

上一篇 下一篇

猜你喜欢

热点阅读