Java中的Handle机制

传送门:https://www.jianshu.com/p/592fb6bb69fa
传送门:https://www.jianshu.com/p/9631eebadd5c

Handler的PostDelayed底层实现原理

Handler的PostDelayed方法参数是一个Runnable对象。delayMillis为延迟时间,PostDelayed函数实际是调用的SendMessageDelayed方法

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

Runnable会被getPostMessage()函数封装在Message对象的Callback属性中。

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

SendMessageDelayed函数会调用SendMessageAtTime, SendMessageAtTime函数会计算出(当前时间+延迟时间 )这个消息需要在什么时间执行作为参数传递。

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

SendMessageAtTime会继续调用enqueueMessage函数

sendMessageAtTime(Message msg, long uptimeMillis)
{
    MessageQueue queue
    enqueueMessage(queue, msg, uptimeMillis);
}

enqueueMessage将消息发送到handle对应的线程消息队列MessageQueue中,并将发送消息的Handler对象封装到message消息对象的target字段。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; // 把发送消息的 handler对象封装到到 msg.target 属性中
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

然后Looper循环器(相当于windows消息机制中的消息循环)会从MessageQueue消息队列中取出Message消息。并通过执行msg.target.dispatchMessage(msg)调用handler对象的dispathchMessage方法。

public static void loop() {
    final Looper me = myLooper(); // 返回当前 looper
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // 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();

    for (;;) { // 循环取出queue 中的msg
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        。。。
        final long end;
        try {
            msg.target.dispatchMessage(msg);    // 使用发送消息的 handler 分发消息
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        .....
        msg.recycleUnchecked();
    }
}

然后handler的dispathMessage方法会判断msg的类型,如果msg.callback不为NULL(也就是我们的Runnable对象),则执行handleCallback(msg)

/*
* 根据 msg 的不同类型分发消息
*/
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {// 
        handleCallback(msg);// 在handler.post(runnable)或 postDelayed(runnable,1000)的时候会执行到这里
    } else {
        if (mCallback != null) { 
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

handleCallback会执行Message.callback.run()也就是Runnable对象的run方法。

需要注意的问题

handle与looper绑定

handle是与looper绑定的,而looper是与创建looper的线程绑定的。创建handle的时候可以指定其所绑定的looper,如果使用无参构造创建handle则其默认使用当前线程的looper与handle绑定。

public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    ......
    //得到当前线程的Looper,其实就是调用的sThreadLocal.get
    mLooper = Looper.myLooper();
    // 如果当前线程没有Looper就报运行时异常
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // 把得到的Looper的MessagQueue让Handler持有
    mQueue = mLooper.mQueue;
    ......  
}

使用handle发送消息时,消息会被发送到其绑定的looper所在的线程,而不是创建handle所在的线程。

子线程调用Toast

线程调用Toast.show需要其先创建looper,因为Toast.show需要通过handle将消息发送到Toast.show调用线程的Looper消息循环中。

posted @ 2022-04-12 23:46  怎么可以吃突突  阅读(227)  评论(0编辑  收藏  举报