android Handler机制解析——sendMessage和handleMessage

4,108 阅读4分钟

Handler是android中跨线程通信的一种方式,最常见的使用场景就是使用handler刷新主线程中的UI界面。 使用方式如下:

    Message msg = Message.obtain();
    msg.obj = "子线程处理好的数据";
    msg.what = 1;
    handler.sendMessage(msg);

通过handler的sendMessage方法我们可以很轻易的将数据从子线程发送到主线程,那么Handler究竟是如何将数据从子线程发送到主线程的呢?

接下来带着问题看源码,从handler.sendMessage()方法入手看Handler内部是如何实现跨线程通信的。 点进sendMessage方法:

从Handler的源码中我们可以知道,handler的sendMessag方法最终调用到了sendMessageAtTime()方法,下面逐行分析一下sendMessageAtTime()方法:
通过上图可以看到sendMessageAtTime()方法最终将消息队列,消息对象,和执行时间传入了enqueueMessage()方法,点进enqueueMessage(),源码如下:
enqueueMessage()又执行了MessageQueue的enqueueMessage方法,继续点下去:

 boolean enqueueMessage(Message msg, long when) {
    //如果msg.target 为空,说明这个msg没有目标handler,抛出异常
    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状态设置成正在使用
        msg.markInUse();
        //设置msg的发送时间
        msg.when = when;
        // mMessages是一个全局的message对象
        Message p = mMessages;
        boolean needWake;
        /**
         * p == null: 说明此时队列中没有msg
         * when == 0: 当前发送的msg是需要立即执行的
         * when < p.when: 当前msg的执行时间小于链表头mMessages的执行时间
         * Message是一个链表结构
         */
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            //将当前 msg 的next 指向 mMessages
            msg.next = p;
            //将当前msg设置为链表中的第一个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.

            // 如果新增的msg的执行时间大于表头message的执行时间,就根据执行时间进行排序
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (; ; ) {
                prev = p;
                p = p.next;
                /**
                 * 直到p == null新增的msg到了队尾
                 * 或者 when < p.when 直到找到比新增的msg执行时间还大的msg,就跳出循环
                 */
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
            // 到这里新增的msg就已经成功加入了队列,并且该队列是根据执行时间进行排序的,执行时间最早的排在队首,最晚的在队尾
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            // 唤醒主线程,native方法
            nativeWake(mPtr);
        }
    }
    return true;
}

关于Message的结构,message是一个链表结构,感兴趣的同学可以自行了解一下。 代码执行到这里,通过sendMessage发送的msg对象就已经加入到MessageQueque中了,在上面的代码中,最后有一段代码:

        if (needWake) {
            // 唤醒主线程,native方法
            nativeWake(mPtr);
        }

这段代码表示的是,通过native方法去唤醒主线程,至于怎么唤醒的,已经深入到了linux底层代码,在这里不做分析。

为什么要唤醒主线程呢?这就涉及到Handler中另外一个重要的对象Looper:

Looper是一个循环器,通过loop方法开启一个死循环

    public static void loop() {
    //获取当前线程中的looper对象,一个线程中只能存在一个looper对象
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }

    //通过looper对象获取到MessageQueue
    final MessageQueue queue = me.mQueue;
    
    ...
    
        //开启一个死循环,从 queue 中不断的取message对象
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
            
             try {
                /**
                 * 获取到的msg对象中有一个target对象,该target对象就是在发送消息的handler
                 * 在Handler的enqueueMessage中对msg.target进行赋值
                 * 执行target的dispatchMessage方法
                 */
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
        ...
        }
    }

可以看到在loop中是有一个死循环的,如果这个死循环一直在执行,是会严重影响系统的性能的。所以当MessageQueue中没有msg对象的时候,死循环执行一段时间后,系统会让主线程休眠(如何休眠?),让出cpu资源,这样就避免了资源的浪费。当有一个msg被添加到MessageQueue中的时候,会去唤醒主线程,告诉主线程,有一个msg进来了,可以去循环取msg了。然后就会执行取msg的操作,并执行msg.target.dispatchMessage(msg)方法,通过上面的分析可以知道target是一个handler对象,接下来到Handler中看看dispatchMessage方法:

public void dispatchMessage(Message msg) {
    /**
     *  如果msg中自带callback不为空执行callback中的方法
     */
   
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //如果Handler的回调不为空
        if (mCallback != null) {
            // 执行Handler的回调
            if (mCallback.handleMessage(msg)) {
                // 如果是使用callback的方式,并在callback中返回true,在这里就会直接return,不会执行Handler的重载方法
                return;
            }
        }
        // 执行Handler的重载方法
        handleMessage(msg);
    }
}

通过上面的分析,就走到了handleMessage方法,到这里就完成了msg从sendMessage到handleMessage的流程,以上是小弟的分析过程,如果有不正确的地方,欢迎各位指正。

android Handler机制解析——Looper和ThreadLoacl: juejin.cn/post/684490…