【Android】Handler 工作流程

本文讲解 Handler 从子线程发送消息(sendMessage)到主线程接收消息(handleMessage)到底都干了些什么?


Handler 通信五大类:

Handler、Message、Looper、MessageQueue、Thread。


这五大类是怎么工作的?

可以把 Handler 的工作流程想象成一个生产线上的传送带,如下图:


首先是发送消息,发送消息可以理解为工人将产品放到传送带的过程。

Handler 发送消息可以是 send 也可以是 post。

如:sendMessage、sendEmptyMessage、sendEmptyMessageDelayed、postDelayed 等。

以 sendEmptyMessage 举例,查看源码是如何发送消息的。

1.Handler 的 sendEmptyMessage 会调用 Handler 的 sendEmptyMessageDelayed。

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

2.Handler 的 sendEmptyMessageDelayed 会调用 Handler 的 sendMessageDelayed。

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

3.Handler 的 sendMessageDelayed 会调用 Handler 的 sendMessageAtTime。

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

4.Handler 的 sendMessageAtTime 会调用 Handler 的 enqueueMessage。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    ...
    return enqueueMessage(queue, msg, uptimeMillis);
}

5.Handler 的 enqueueMessage 会调用 MessageQueue 的 enqueueMessage。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    ...
    return queue.enqueueMessage(msg, uptimeMillis);
}

所以,所有的消息,不管调用的是哪个 send 或 post,最终都是将消息发送到消息队列里。


Q:MessageQueue 的 enqueueMessage 干了什么事情?

A:MessageQueue 的 enqueueMessage 就是存储了 Message ,也就是把消息放到消息队列里,而 MessageQueue 的 enqueueMessage 存储消息的方式是一个优先级队列(根据时间进行排序的队列)。

(优先级队列是用链表结构构成的,链表是数据的保存方式,只不过链表里面加入了队列算法(先进先出),而在先进先出的算法基础之上又加入了根据时间进行排序,就变成了优先级队列。)

通过源码看具体逻辑实现:

boolean enqueueMessage(Message msg, long when) {
    ...

    synchronized(this) {
        ...

        // 根据时间进行排序(你添加的 when ,和消息队列的时间节点进行排序)
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            ...

            // 排序完了之后,找到插入的点,把这个消息放进去
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }

        ...
    }
    return true;
}

可以看到所有的消息都是根据时间进行排序,这样就形成了一个消息队列,队头的消息最早执行,队尾的消息最晚执行。也叫优先级排序。


Q:这个时间怎么理解?

A:以 Handler 的 sendMessage 举例。

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

如果没有设置时间,那么就是当前时间,意味着立刻执行。那么给 sendMessageDelayed 传入的参数就是 0。

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

那么时间就是 SystemClock.uptimeMillis(系统时间)+ delayMillis 时间。所以每一个消息都会有一个执行时刻。

那么这样我们就可以知道消息一旦发送出去之后就会存在于我的 MessageQueue 里面,那么有消息进来了自然就会有消息出去。


Q:这个消息怎么出去?

A:还是拿传送带举例,工人(sendMessage )把产品(Messgae) 放到了传送带上 ,传送带可以理解为 MessageQueue ,传送带滚动需要提供动力,Looper 就是给它提供动力的函数。提供动力的开关是 Looper.loop() ,或者说动力通上电是通过线程(Thread)调用线程所对应的 Looper 的函数。

Looper.loop() 源码

public static void loop() {
    ...

    // 死循环
    for (;;) {
        // 从消息队列里取消息
        Message msg = queue.next();
        if (msg == null) {
           return;
        }
        
        ...

        try {
            msg.target.dispatchMessage(msg);
         	  ...
        } catch (Exception exception) {
            ...
        } finally {
            ...
        }
        
        ...
    }
}

MessageQueue.next() 源码

Message next() {
    ...

    for (;;) {
        ...
      
        synchronized (this) {
            ...

            if (msg != null) {
                if (now < msg.when) {
                    ...
                } else {
                    ...

                    return msg;
                }
            } else {
                ...
            }

            ...
        }

        ...
    }
}

next 返回值是一个 msg。next 函数就是从这个优先级队列(传送带)里取消息。也就是通上电之后从队头取消息。

Handler.dispatchMessage() 源码

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        ...
    } else {
        ...

        handleMessage(msg);
    }
}

工作流程:传送带通电(Looper.loop),传送带滚轮滚动(MessageQueue.next),滚动之后后就会从消息队列里取消息(Handler.dispatchMessage)。


Q:那么怎样让它停下来呢?

A:查看 Looper.loop() 源码

public static void loop() {
    ...

    // 死循环
    for (;;) {
        // 从消息队列里取消息
        Message msg = queue.next();
        
        ...
    }
}

如果消息队列为空,那么 queue.next() 就会被 block 住。那么 Message msg = queue.next() 就会有一个睡眠,外层的 for 死循环就会停下来。

如果本文对你有帮助,请点赞支持!!!

猜你喜欢

转载自blog.csdn.net/cnwutianhao/article/details/107587733