【Android】线程间通信 - Handler之源码篇(3)

·  阅读 950
【Android】线程间通信 - Handler之源码篇(3)

我正在参加「掘金·启航计划」

00、前言

前面两篇源码篇内容,你睇左咩呀(你看了没呀)?没有的话可以通过下面的传送门,去瞅瞅先。

今天就接着上面两篇来看看,最后消息是如何被处理的吧!也就是如下图红色箭头所指示的流程

image.png

01、用法

我们可以通过下面 3 种方式来处理消息

第一种

通过 post 系列的方式发送消息时:

mHandler.post(object : Runnable {//
    override fun run() {
        // TODO  具体业务逻辑
    }
})
复制代码

第二种方式

在创建 Handler 时,传入一个 Callback:

private val mHandler2 = object : Handler(object : Callback {
    override fun handleMessage(msg: Message): Boolean {
        if (msg.what == 1) {
            return true
        }
        return false
    }
}) {
}
复制代码

第三种方式

重写 HandlerhandleMessage 方法:

private val mHandler3 = object : Handler() {
    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)
    }
}
复制代码

注:这三种方法我们可以同时实现,但是有时候并不会都收到回调。那是为什么呢?让我们一起从源码上分析一下吧。

02、源码

我们从后往前分析。即,先看 Handler 如何分发消息,再看 Looper 如何从 MessageQueue 中拿到要处理的消息。

分发消息

//Handler.java

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); //标识 1
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) { //标识 2
                return;
            }
        }
        handleMessage(msg);//标识 3
    }
}
复制代码

这个方法的逻辑就是:如果 msg.callback 不为空,就调用 handleCallback() 处理消息,然后结束。否则如果 mCallback 不为空,而且调用 mCallback.handleMessage() 后,返回的是 true,则结束,否则再次调用 handleMessage()

handleCallback()

那么我们跟进一下上面的三个标识

//标识 1
private static void handleCallback(Message message) {
    message.callback.run();
}
复制代码

可以看到这里最后调用了 Messagecallback,而在 post 中,就是将我们传入的 Runnable 复制给了 MessagecallbackRunnable 类型) 变量。

mCallback.handleMessage()

标识 2 中,我们主要看看这个 mCallback 是什么?追溯后发现就是在使用方法中的方式二,在创建 Handler 传入的 Callback 参数。

private val mHandler2 = object : Handler(object : Callback {
    override fun handleMessage(msg: Message): Boolean {
        if (msg.what == 1) {
            return true
        }
        return false
    }
}) {
}
复制代码

同时这里返回的 truefalse 将决定是否会继续执行标识 3。

handleMessage()

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(@NonNull Message msg) {
}
复制代码

即对应这我们使用的第三种方式,重写 Handler 上述方法。

关系对应

dispatchMessage 方法使用层对应的方式
handleCallback()方式一:使用 post 发送消息
mCallback.handleMessage()方式二:创建 Handler 传入 Callback 参数
handleMessage()方式三:重写了 Handler 的 handleMessage 方法

获取消息

上面完成了图中 ② 的过程,那么 ① 中 Looper,是如何获取到要被处理的消息的呢?

那就要从 Looper.loop() 方法开始分析了。当我们调用了这个方法后,Looper 就会开始从 MessageQueue 中不断地获取到需要被处理的消息。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }

    me.mInLoop = true;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // Allow overriding a threshold with a system prop. e.g.
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    me.mSlowDeliveryDetected = false;

    for (;;) { // 标识 4
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}
复制代码

最关键的是代码中的标识 4,一个死循环。(不同源码版本,可能略有不同,但主要逻辑相同,这里是 Android API 32)

//标识 4
//Looper.java
private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next(); // 标识 5 ,可能会发生堵塞
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return false; //如果 msg 为空,意味着 MessageQueue 已经退出了。停止循环获取消息。
    }
    //...
    try {
        msg.target.dispatchMessage(msg); //消息分发,这里的 target 就是发送消息的 Handler
      //...
    } catch (Exception exception) {
     //...
    } finally {
       //...
    }
    //...
    msg.recycleUnchecked(); //回收消息
    return true; //返回 true,则继续循环获取消息。
}
复制代码

为了突出本次讨论的重点,部分无关代码已省略。

首先我们看一下两个 return 的地方。当获取到的 msg 为空时,返回 false。即结束外面的死循环。否则最后都是返回 true,则继续通过死循环获取 msg

在这里主要内容在标识 5 上,获取一个消息。我们继续跟进到 MessageQueue

//标识 5 
//MessageQueue.java

Message next() {
    //...
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 阻塞(这里就开始涉及到 epoll 机制)。
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                  //如果还没到执行时间,那么就设置一个唤醒阻塞的延时时间。这个外层的 for(;;) 就是因为要处理这种延时消息
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    // prevMsg 不为空,说明要处理异步消息,从消息队列中剔除这个异步消息。(与同步屏障有关)
                    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; //取得有效 msg,并返回。
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
           //...
    }
}
复制代码

在这里主要是通过一个 for(;;) ,获取到需要处理的消息并返回到 Looper 中。

03、结语

到这里,关于 Handler 的消息分发机制源码分析。也就先告一段落了。源码中还涉及到一些其他内容,将在后续的番外篇继续更新。例如:同步屏障等。

分类:
Android
收藏成功!
已添加到「」, 点击更改