源码揭秘,Handler 的 post 与 sendMessage 方法的本质区别

880 阅读5分钟

hi 大家好,我是 DHL。就职于美团、快手、小米。公众号:ByteCode,分享有用的原创文章,涉及鸿蒙、Android、Java、Kotlin、性能优化、大厂面经。

在 Android 中 Handler 是用来处理线程间通信的一种机制,而 Handler 的 post(Runnable r) 和 sendMessage(Message msg) 方法都是处理线程间通信的重要方法,在面试中也是高频面试题。

它允许你发送一个 Message 或者 Runnable 对象到 Handler 关联的消息队列(MessageQueue)中。但是它们在使用方式、应用场景以及内部实现上有所区别。

内部源码实现不一样

post (Runnable r)

当调用 post(Runnable r) 方法时,会调用 getPostMessage(r) 创建一个 Message 对象,将 Runnable 对象赋值给 Message 的 callback 字段。

public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}
    
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

然后调用 sendMessageDelayed(Message msg, long delayMillis) 方法将其发送到消息队列中。这个过程是同步的,即立即将消息加入队列中。

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    // ...省略部分代码...
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // ...省略部分代码...
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // ...省略部分代码...
    boolean sent = queue.enqueueMessage(msg, uptimeMillis);
    // ...省略部分代码...
    return sent;
}

sendMessage (Message msg)

而 sendMessage(Message msg) 方法直接将 Message 对象加入到 MessageQueue 中,如果需要延迟发送,也可以通过 sendMessageDelayed(Message msg, long delayMillis) 实现, Message 对象可以携带数据。

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

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    // ...与post相同的实现...
}

// sendMessageAtTime和enqueueMessage与post中的实现相同

两者的主要区别在于:

  • post(Runnable r) 用于发送一个 Runnable 任务,将 Runnable 对象赋值给 Message 的 callback 字段,然后将其发送到 MessageQueue 中。

  • sendMessage(Message msg) 用于发送一个可以携带数据的 Message 对象到 MessageQueue 中。

无论通过那种方式,最终都会生成 Message 对象加入到 MessageQueue 中,然后 Looper 从消息队列中取出消息,分发给 Handler 的 dispatchMessage(Message msg) 方法分发处理。这两种消息虽然都在 dispatchMessage 方法中,但是消息的优先级不一样。

消息的优先级不同

Handler 的 dispatchMessage(Message msg) 方法是用来分发消息的核心逻辑。

当一个消息被 Looper 从 MessageQueue 中取出后,Looper 会调用 Handler 的 dispatchMessage(Message msg) 方法来处理这个消息。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        // 如果消息中有Runnable回调,则执行它
        handleCallback(msg);
    } else {
        // 如果消息没有Runnable回调,则直接处理消息
        if (mCallback != null) {
            // 如果Handler有Callback,则尝试由Callback处理
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 如果没有Callback或Callback没有处理消息,则由handleMessage方法处理
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
    message.callback.run();
}

public void handleMessage(Message msg) {
    // 需要由子类覆盖此方法来处理消息
}

在 dispatchMessage(Message msg) 方法中,首先检查 Message 对象的 callback 字段是否为空。如果不为空,说明这个消息是通过 post(Runnable) 方法发送的,因此它会直接执行这个 Runnable。

如果 Message 对象的 callback 字段为空,那么 Handler 会检查 mCallback(一个 Handler.Callback 对象) 是否为空,如果不为空,并且 mCallback 的 handleMessage() 方法返回 true,表示消息已被处理,流程结束。

Handler.Callback callback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // 处理消息
        return true; // 如果消息被处理,返回true
    }
};

// 创建Handler时传入Callback
Handler handler = new Handler(callback);

如果 mCallback 为空,或者 mCallback 的 handleMessage() 方法返回 false,那么最后会调用 Handler 的 handleMessage(Message msg) 方法来处理这个消息。我们只需要在自己的 Handler 子类中覆盖 handleMessage(Message msg) 方法即可。

public class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        // 在这里处理消息
        // 通过检查`Message`对象的`what`字段来区分不同类型的消息,并进行相应的处理
    }
}

如果你在非主线程中创建 Handler 的实例,你需要确保该线程已经准备好了 Looper,否则 Handler 将无法正常工作.

使用场景不一样

post (Runnable r)

通常用于在指定的线程(通常是主线程)中执行一段代码,无需传递数据或者需要传递的数据可以通过外部类或者闭包来提供。

sendMessage (Message msg)

适用于需要传递数据或者需要跨线程通信的场景,通过 Message 对象可以携带数据,并由 Handler 在目标线程中处理这些数据。

总结来说,post(Runnable r) 适用于简单的任务,而 sendMessage(Message msg) 适用于需要传递数据和复杂线程间通信的场景。

全文到这里就结束了,感谢你的阅读,如果文章对你有帮助,欢迎 在看、点赞、分享 给身边的小伙伴,你的点赞是我持续更新的动力。


更多大厂面试题,欢迎前往微信搜索小程序「猿面试」查看。微信小程序(猿面试)包含了 Java、Android、鸿蒙和ArkTS、设计模式、算法和数据结构 相关内容,

Hi 大家好,我是 DHL,大厂程序员,公众号:ByteCode ,在美团、快手、小米工作过。分享有用的原创文章,涉及鸿蒙、Android、Java、Kotlin、性能优化、大厂面经。

推荐阅读:

开源新项目

  • 云同步编译工具(SyncKit),本地写代码,远程编译,欢迎前去查看 SyncKit

  • KtKit 小巧而实用,用 Kotlin 语言编写的工具库,欢迎前去查看 KtKit

  • 最全、最新的 AndroidX Jetpack 相关组件的实战项目以及相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看 AndroidX-Jetpack-Practice

  • LeetCode / 剑指 offer,包含多种解题思路、时间复杂度、空间复杂度分析,在线阅读