Handler机制源码详解

2020-10-02  本文已影响0人  CoderCyl

Handler对于Android系统来说可以是非常重要的了,因为它贯穿了整个Android系统,它随处可见,没有Handler,Android系统也就不起作用了。

流程介绍

Android系统是以消息驱动的,整个流程可以大致概括为:

发送消息 --> 添加消息到队列 --> 从队列中获取消息 --> 处理消息

上面的流程可以引出几个类分别是,发送消息的Handler,将消息Message发送到消息队列MessageQueueLooper从消息队列循环取出消息,然后交给Handler处理。

在这里插入图片描述

源码解析

发送消息

发送消息有sendpost的方式:

// 发送消息
Handler handelr = new Handler();
// 发送方式一
handler.sendMessage(msg); // 或者handler.sendEmptyMessage(what);

// 发送方式二
handler.postXXX(runnable);

handler.sendXXX()handler.postXXX()最终都会调用到sendMessageDelayed()方法。

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    // 注意sendMessageAtTime方法的第二个参数,当前时间加上延时时间
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

然后调用了sendMessageAtTime方法

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis); // uptimeMillis = SystemClock.uptimeMillis() + delayMillis
}

又调用了enqueueMessage()方法

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis); // uptimeMillis = SystemClock.uptimeMillis() + delayMillis
}

然后又调用了MessageQueueenqueueMessage()方法

boolean enqueueMessage(Message msg, long when) { // when = SystemClock.uptimeMillis() + delayMillis
    ... // 省略无关代码
    synchronized (this) {
        ...
            
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // 当消息队列为空或者将要入队的消息(msg)的时间(when)在所有消息队列的消息最前面,则把msg插入到队头,最先执行
            msg.next = p;
            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.
            // 消息队列已经不为空了,再插入一个消息时,要先遍历所有的消息,根据时间先后排序,决定将新消息插入在什么位置
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    // 当p为空(遍历到最后一个了),或者新消息的时间在p的时间之前,就不用再遍历了,因为可以确定新消息要插在消息p前面了
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            
            // break跳出for循环后执行到这里,将msg插入到p前面,prev后面
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }
        ...
    }
    return true;
}

Message类的实现可以看出它是个单链表,消息队列中的消息都是按照时间先后顺序链接起来的。

接下来详细分析一下上述代码,在if分支中有三个条件判断p == null || when == 0 || when < p.when

以上三个条件只要满足任一条件都会执行到if代码块中的语句,将新消息添加到p消息的前面,p消息为空时,即队头。

当添加新消息时,消息队列中是有消息的,就会执行到else语句中。先来看看下面代码是什么意思:

prev = p;
p = p.next;

第一行代码中的p就是在if代码块中添加的消息,说明指针指向p消息,第二行代码又将指针指向了p消息的下一个消息。没错,指针向后移动了一位。为什么要这样呢?

从上面2行代码中可以看出,再次添加消息时,遍历消息都是从添加if代码块中的消息开始的,因为从if代码块中添加的消息要么就是第一个消息,要么就是when小于p消息的wnen的,也就是说可确保前面的消息都是时间有序递增的。而系统的时间是一直在增加的,所以enqueueMessage()方法的第二个参数when一直大于mMessageswhen,所以要把指针向后移一位,从后面一个消息开始比对。

上图加深理解(图中所有msgwhen为了偏于比较和理解,都是用的比较小的数字,而没有使用时间戳):

在这里插入图片描述

添加第一个消息时,消息队列中是没有消息的,直接将消息msg1添加到消息队列的头部。

在这里插入图片描述

当添加第二个消息时,消息队列中已经有消息msg1,此时不满足if的条件,执行到else中的代码,因为p = mMessages,而mMessages在添加第一个消息时被赋值成了msg1,代码p = p.next将指针指向了msg1的下一个,也就是null,即p = null。当p == null时会跳出循环,会执行到

msg.next = p
prev.next = msg

也就是将msg2的下一个节点设置成p,即将msg2消息插入到p的前面。如下图:

在这里插入图片描述

上面添加的两个消息不是插入到队头就是队尾,再插入一个到中间的消息看看代码是怎么执行的。


在这里插入图片描述

因为mMessage只在添加msg1的时候被赋值了,所以p = mMessage = msg1,接下来执行if的判断条件if (p == null || when == 0 || when < p.when)p = msgwhen = 4p.when = 1,不满足if条件,执行到else中的代码。

再来看else中的for循环,p = p.next将指针移到了msg2的位置,此时p = msg2when = 4p.when = 5,满足了for循环的if条件when < p.when

for (;;) {
    prev = p;
    p = p.next;
    if (p == null || when < p.when) { // 满足了该条件,跳出循环
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false;
    }
}

跳出循环后,执行代码

msg.next = p; // invariant: p == prev.next
prev.next = msg;

msg3插入到msg2前面,至此msg3的插入流程就走完了。

突然发现,好像除了添加msg1的时候执行到了if中的代码,后面都是执行else的代码。那在什么情况下会再次执行if中的代码呢?从if的条件中可以看出,此时p = msg1不会为空,when实际上是当前时间的时间戳,也不会等于0,所以只有当when < p.when的时候才会再次执行到if中的代码。

那再次发送一个消息msg4,假设msg.when = 0(实际上不会为0,这里为了方便假设为0,这个0不等于if条件中的0),满足条件后执行

msg.next = p;
mMessages = msg;

msg4插入到p的前面,也就是msg1的前面了,然后又刷新了mMessages的值了。

在这里插入图片描述
总结

取出消息

开头说过整个系统是以消息驱动的,那么Looper就是其动力了,Looper.loop()方法不断的从MessageQueue中取消息。那么Looper.loop()方法是在哪里调用的呢?由于系统都是由消息驱动的,所以在系统启动的时候就应该有动力驱动了,在SystemServer.main()中调用了Looper.loop(),这是在系统层面的驱动,而对于APP应用层面来说,应用入口ActivityThread.main()中也需要调用Looper.loop(),毕竟APP也是需要各种事件响应的嘛。来看看loop()的代码:

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; // 获取消息队列,获取到了消息队列后才能从消息队列中取出消息

    ... // 省略无关代码

    for (;;) {
        Message msg = queue.next(); // 获取消息,无消息时可能会阻塞
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        ... // 省略无关代码
            
        // Make sure the observer won't change while processing a transaction.
        final Observer observer = sObserver;

        ... // 省略无关代码

        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting(); // 开始分发消息
        }

        try {
            msg.target.dispatchMessage(msg); // 分发处理消息
            if (observer != null) {
                observer.messageDispatched(token, msg); // 消息分发处理完成
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception); // 消息分发异常
            }
            throw exception;
        } finally {
        }
        ... // 省略无关代码
        msg.recycleUnchecked(); // 回收消息
    }
}

首先要获取到Looper对象,当Looper对象为空时抛出异常No Looper; Looper.prepare() wasn't called on this thread.,我们就知道调用loop()方法之前需要先调用prepare()方法,在prepare()中创建出了Looper对象。

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

在创建Looper时创建了MessageQueuesThreadLocal保证了一个线程只有一个Looper,而一个Looper又只有一个MessageQueue

插入话题来看看ThreadLocal是怎么做到这些的。

回头看看prepare()方法,首先调用sThreadLocal.get()获取Looper,如果不为空说明已经创建过Looper了,为空就创建一个Looper保存在sThreadLocal中,看看sThreadLocal.get()的实现:

// 获取泛型T,这里是Looper
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

// 获取当前线程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

// 当ThreadLocalMap为空时,设置初始值
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

protected T initialValue() {
    return null;
}

// 创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

获取到当前线程,然后根据当前线程获取ThreadLocalMapThreadLocalMapThread的一个变量,表明一个线程只有一个ThreadLocalMap。接着判断ThreadLocalMap是否是空,为空则创建一个,否则就获取ThreadLocalMap中的值。再来看看sThreadLocal.set(T)方法:

# ThreadLocal.java
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value); // this就是sThreadLocal,static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    else
        createMap(t, value);
}

set()方法中先是获取了当前线程,然后根据当前线程获取ThreadLocalMap,当map为空时就根据当前线程创建一个,否则就将value设置到map中。从map.set(this, value)可以看出,是用当前ThreadLocal对象sThreadLocal作为keysThreadLocal是所有线程共享的,一个线程只有一个ThreadLocalMap,而一个ThreadLocalMapkey = this时只能获取到一个value

再回头看看Looper.prepare()方法中的sThreadLocal.get()方法,从sThreadLocal.set()方法中我们已经知道了Looper和线程是一一对应的关系,从当前线程中获取的Looper不为空时就抛出异常,这样就避免了Looper再次创建。

在这里插入图片描述

话题转回来,获取到了looper对象后,就从looper中获取MessageQueue对象queue,然后在for(,,)循环中调用queue.next取消息,当消息队列中没有消息时,就会阻塞在queue.next()这里。来看看queue.next()中做了什么:

Message next() {
    ... // 省略无关代码
        
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        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;
            // 当msg,target == null,表示这是同步屏障,msg就是优先执行的消息。
            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) {
                // 代码片段1
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    // 当前时间大于消息的时间,说明还没有到该消息执行的时候,计算出消息执行的延时时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 代码片段2
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next; // msg将要进行处理,然后会将处理的消息删除,所以消息指针需要向后移一位
                    }
                    msg.next = null; // 表示将msg从消息队列中剔除
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1; // 没有消息,继续休眠等待
            }

            ... // 省略无关代码
        }

        ... // 省略无关代码
    }
}

next()里面是一个死循环,enqueueMessage()next()方法中都用了同步锁,避免在发送消息时取消息以及取消息时发送消息,保证了线程安全。主要看synchronized(this)中的代码,首先获取到msg,就拿下图中发送的消息来看,此时的msg= mMessages = msg4msg不为空,执行到<u>代码片段1</u>处,然后比较当前时间和msg.when的大小,如果now < msg.when,表明还没有到执行该消息的时候,计算出延迟执行时间;否则就执行<u>代码片段2</u>,prevMsg为空,所以执行mMessages = msg.next,将指针后移一位,即下图中的msg1处,然后执行msg.next = null,将msg4消息剔除。

在这里插入图片描述

这里要注意的是msg.next = null并不是将msg1设置为空的意思,而是将msg4与后面的消息节点断开连接。看下图

在这里插入图片描述

获取到消息之后会将消息返回,然后又回到了Looper.loop()方法的for(;;)中,返回的消息就是Message msg = queue.next();中的msg,然后将msg分发给handler处理,再次看上面的loop()方法,下面贴出关键代码:

try {
    msg.target.dispatchMessage(msg); // 分发处理消息
    if (observer != null) {
        observer.messageDispatched(token, msg); // 消息分发处理完成
    }
} catch (Exception exception) {
    ...
} finally {
    ...
}

调用到msg.target.dispatchMessage(msg)方法,那msg.hander是什么呢?其实它就是Handler,还记得handler.enqueueMessage()方法吗?

#Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this; // 在这里设置了target
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
总结

处理消息

Handler类中发送消息的时候就设置了msg.targetthis指的就是当前的Handler对象。再次回到了Handler中,看看dispatchMessage做了什么事情。

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

msg.callback是在创建Message的时候赋值的,而mCallback是在构造Handler的时候赋值的

public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback; // 给msg.callback赋值

    return m;
}
public Handler() {
    this(null, false); // 默认callback为空
}

public Handler(@Nullable Callback callback, boolean async) {
    ... // 省略无关代码
    mQueue = mLooper.mQueue;
    mCallback = callback; // 给mCallback赋值,默认为空
    mAsynchronous = async;
}

因此msg.callbackmCallback都为空,接下来执行到handleMessage(msg)方法

public void handleMessage(@NonNull Message msg) {
}

handleMessage方法是一个空实现方法,具体的实现在Handler的回调中

Handler handler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        // 实现具体的逻辑
    }
};

扩展

dispatchMessage()中处理消息的地方有三处,默认调用的就是handleMessage(msg),那什么时候调用其它2种方式呢?

msg.callback不为空就会执行handleCallback(msg),那么就需要先给msg.callback赋值了。上面说过msg.callback是在创建Message的时候赋值的,创建Message是通过Message.obtain()来创建的,来看看obtain方法的重载方法:

public static Message obtain(){}
public static Message obtain(Message orig){}
public static Message obtain(Handler h){}
public static Message obtain(Handler h, Runnable callback){}
public static Message obtain(Handler h, int what){}
public static Message obtain(Handler h, int what, Object obj){}
public static Message obtain(Handler h, int what, int arg1, int arg2){}
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj){}

obtain有8个重载方法,第四个重载方法可以设置msg.callback,那在创建Message的时候可以这样写:

Handler handler = new Handler();
Message message = Message.obtain(handler, new Runnable() {
    @Override
    public void run() {
    }
});
handler.sendMessage(message); // 发送消息

Message obtain(handler, callback)中对msg.callback进行了赋值

public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback; // 赋值
    return m;
}

然后在分发消息时会执行到handleCallback(msg)

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

最终会回调到创建Message时传入的第二个参数Runnablerun方法中。

msg.callback的优先级最高,其次是mCallback,只有在msg.callback为空时才会执行到mCallback.handleMessage(msg),所以在创建Message的时候不能再传入callback了。mCallback是在构造Handler的时候赋值的,所以要在创建Handler的时候就要传入mCallback。来看看Handler的构造函数:

public Handler(){}
public Handler(@NonNull Looper looper){}
public Handler(@NonNull Looper looper, @Nullable Callback callback){}
public Handler(boolean async){}
public Handler(@Nullable Callback callback){}
public Handler(@Nullable Callback callback, boolean async){} // @hide
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async){}

`

Handler有7个构造函数,我们可以通过最简单的第五个构造函数来创建Handler

Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
        return false;
    }
});
Message message = Message.obtain(handler);
handler.sendMessage(message);

Handler的构造函数中对mCallback进行了赋值,最终会调用到第六个被标上@hide的构造函数:

public Handler(@Nullable Callback callback, boolean async) {
    ... // 省略无关代码
    mQueue = mLooper.mQueue;
    mCallback = callback; // 赋值
    mAsynchronous = async;
}

此时,msg.callback为空,mCallback不为空,就会调用mCallback.handleMessage(msg)方法,最后回调到了创建Handler的参数Handler.CallbackhandleMessage(msg)中。

上一篇 下一篇

猜你喜欢

热点阅读