Handler源码分析(一)

5,104 阅读6分钟

本文讲解Handler的源码,ok,现在先来看Handler的使用

Handler handler = new Handler(Looper.getMainLooper());
handler.sendMessage(message);

1 构造函数分析

首先我们来看构造函数,构造函数有很多个重载版本,无非就是无参的调用有参的,少参的调用多参的,这是很常用的一个套路,比如View的构造函数就是这样,这样的好处就是,只需要在最多参的构造函数中 调用一次初始化操作就可以,其他少参的或无参的也会初始化到。现在我们来看最多参的构造即可:

 public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

嗯,很简单,初始化了mLooper, mQueue,mCallback,以及一个mAsynchronous的boolen值,下面对这些变量进行讲解: mLooper: 一个Looper,用来对消息进行循环检测,有消息就取出处理,没有就等待 mQueue: 一个MessageQueue,是一个队列,用来保存Message mCallback: 一个callback,用于触发Handler的回调,下面是他的定义,都很熟悉的,不再废话

public interface Callback {
    boolean handleMessage(@NonNull Message msg);
}

isAsynchronous:一个boolean值,用来定义消息是否是异步的,这个后面再讲。 好,现在我们来看第一个参数looper从哪里来,点击Looper.getMainLooper(),可以看到:

public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
}

哦,这里直接return了sMainLooper,然后看sMainLooper被赋值的地方:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

这里先调用perpare(false),然后做个初始化判断,如果已经初始化过了,就抛出异常,然后对sMainLooper进行赋值,好,我们先不看prepare(),只看myLooper():

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}

就这一句,既然有get(),肯定有set(),我们找找这个sThreadLocal在哪里set()的:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}

哦,原来在prepare()里面set的,这就对上了,在prepareMainLooper()里面,先使用prepare(false),创建一个Looper然后使用sThreadLocal.set(looper),保存到sThreadLocal中, 然后使用sThreadLocal.get()赋值给sMainLooper。这里有个问题:为什么不这样写:

sMainLooper = new Looper(quitAllowed);

多简单!!!而非要:

sThreadLocal.set(new Looper(quitAllowed));
sMainLooper = sThreadLocal.get();

这个问题我们先记录一下:问题1 为什么Android要把Looper存在ThreadLocal里面?,先回到我们的主线,我们现在绕了一大圈,找到了第一个Handler的第一次个参数mLooper在这里被new出来了, 我们看看new Looper(quitAllowed);做了什么:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

哦,第一:创建了一个MessageQueue();第二:保存了当前线程。 那这个线程是哪个线程啊?就是new Looper()的那个线程啊。 new Looper()是在prepare()中调用的,prepare()是在prepareMainLooper()中调用的,那就是说这个线程就是prepareMainLooper()被调用的线程,那prepareMainLooper()在哪里被调用的? 哦,在这里,ActivityThread的main方法中:

public static void main(String[] args) {
        ...
        省略若干代码
        ...
        Looper.prepareMainLooper();
        ...
        省略若干代码
        ...
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

哦,找到了,这个ActivityThread.main()又是在哪里被调用的?这个是在当前进程被创建的时候调用的,也就是Android应用进程的入口,这个后面再说,现在知道了 Looper中的线程就是这个线程,这个线程是谁呢?就是UI线程,也就是主线程!可以先留个问题:问题2 ActivityThread.main()在哪里被调用? 好,现在我们来总结一下:我们找到了Handler的第一个参数Looper被new出来了,然后他保存了当前的线程,然后还new出来一个MessageQueue(),那么我们看看MessageQueue()的构造吧:

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

很简单,就保存了一个参数,然后调用了一个nativeInit()方法,Handler的第一个参数mLooper已经初始化了,现在我们来看第二个参数mQueue:

mQueue= looper.mQueue;

就是把looper的mQueue拿过来保存一下,那第三个参数callback和第四个参数呢,都是保存一下ok了,好,Handler的构造函数完毕。说白了就是: 1 创建Looper并保存 2 创建MessageQueue并保存


2 调用流程分析

我们怎么使用Handler的呢?很多方法,要么: 1 handler.sendMessage(message);要么:2 handler.post(runnable),当然还有其他的使用方法,其实道理都是一样的,我们来看最普遍的用法: handler.sendMessage(); 这里先放一个Message的简略类信息:

public final class Message implements Parcelable {
    public int what; //不BB了,都知道的
    public long when; //指定此message执行的时间,到了when这个时间就需要执行
    int flags; //用于控制状态
    Handler target; //被哪个Handler执行
    Runnable callback; //回调
    Message next; //指向下一个Message
    ***省略其他成员变量***
}

点进去看:

public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0); //这里直接把0当作delay传入
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); //这里用当前时间+需要延迟的时间传入
}

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); // 这个queue就是第一步初始化Looper时候的那个
}

又是3连调,我们只看sendMessageAtTime(msg, uptimeMillis)即可,其实就是一句话: enqueueMessage(queue, msg, uptimeMillis);继续看:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
    msg.target = this; //将msg.target赋值为当前Handler
    if (mAsynchronous) { //这个我知道,Handler构造函数的第四个参数
        msg.setAsynchronous(true); // 这tm是啥意思,算了不管了
    }
    return queue.enqueueMessage(msg, uptimeMillis); //这queue是MessageQueue,就是Looper被new出来创建的那个
}

逻辑也很简单: msg.target = this,保存了当前Handler,然后又调用了MessageQueue的enqueueMessage(),好,我们看下这个函数:

boolean enqueueMessage (Message msg,long when){ //这里的when指的是要执行的时间
    ***省略部分代码***
    synchronized (this) {
        msg.markInUse();
        msg.when = when; //保存要执行的时间,系统时间在when的时候,需要执行这个msg
        Message p = mMessages; //这是一个成员变量: Message mMessages,保存当前的消息队列的队头
        boolean needWake; //是否需要唤醒

        if (p == null || when == 0 || when < p.when) {
            //我们看下这个if条件:
            //1 p == null就意味着 mMessages == null; 也就是说队头是null,也就是说本次enqueue的msg是第一个消息
            //2 when == 0,这个不可能啊,when是怎么来的,我们回忆一下,是在sendMessageAtTime()里面,用当前时间加上delay的延迟时间,怎么会是0,除非故意传递0,很可能系统
            //内部调用的,先不看
            //3 when < p.when,那就是msg.when < mMessages.when,也就是说这个msg应该在队头执行之前执行的
            //上面条件都说明:这个msg需要在队头之前执行,那是不是要插入到队头呢,看下面代码:

            msg.next = p; //嗯,这个msg的next是p
            mMessages = msg; //然后队头指向了这个msg,哦,明白了,就是说这个msg是第一个消息,就插到队头,ok了解
            needWake = mBlocked; //如果当前阻塞了,需要唤醒,唤醒啥?后面再说,反正记住要唤醒就行

            //**FLAG1** 看过MessageQueue.next()再回来看这里!!! 好,我们回来了,刚刚分析了一波如果没有消息需要处理就阻塞,现在这里明显有消息需要处理了,那么如果阻塞,就需要唤醒,
            //所以needWake = mBlocked; 阻塞了就需要唤醒,否则不需要。好,去看FLAG2
        } else {
            //这里就是不需要插到队头的了
            needWake = mBlocked && p.target == null && msg.isAsynchronous(); // 这个条件不用看了,系统使用的,就当false就行,以后分析
            //此处往下是重点
            Message prev;
            for (; ; ) {
                prev = p; //prev指向队头
                p = p.next; //队头指向下一个
                if (p == null || when < p.when) {
                    //1 p == null,除非是最后一个message了,也就是遍历完了
                    //2 when < p.when,那就是说这个msg在p之前执行了
                    break;
                }
                if (needWake && p.isAsynchronous()) { //这个先不用看了,系统使用的,就当false就行,以后分析
                    needWake = false;
                }
            }
            msg.next = p; //遍历结束,msg在p之前执行,就将msg插入到p前面,所以msg.next = p;
            prev.next = msg; //prev.next本来是p的,现在指向了msg,那就是将msg插入到prev和p之间了
            //通过上面分析,可以看懂messageQueue是按照when的大小排序的,越早执行的越靠前
        }
        if (needWake) { //如果需要唤醒,就唤醒,目前我们看到的,只有在if里面才可能为true,也就是needWake = mBlocked,后面再分析mBlocked
            nativeWake(mPtr);
            //**FLAG2** ok我们回来了,如果next()里没有return msg;那么就会被阻塞,也就是会调用nativePollOnce()阻塞,
            //那么此时有消息需要处理就使用nativeWake()来唤醒了,此时我们反复看enqueueMessage()这个函数和next()对比,就发现是成对的,一个存放msg,一个取出msg,
            //然后通过Looper.loop()来检测,很好的机制
        }
    }
    return true;
}

这里删除了部分代码,我们只看关键部分,仔细看注释,我们发现,整个流程就是将msg按照执行时间顺序插入到队列,越早执行的越靠前,如果msg是第一条消息(p == null), 或者需要立刻执行(when == 0||when < p.when),就会立刻唤醒,调用nativeWake(mPtr)去唤醒native执行,那么这个nativeWake(mPtr)是什么玩意呢,这里可以先简单的 理解为java层的notify()即可。那么我们把消息存入到MessageQueue里面了,从哪里取出来使用呢,我们回忆一下我们第一步的Looper的构造,是在ActivityTrhead.main() 函数里面,里面就两句:

public static void main(String[] args) {
    ...
    省略若干代码
    ...
    Looper.prepareMainLooper();
    ...
    省略若干代码
    ...
    Looper.loop();

    这里有个异常,看来是跑不到,说明Looper.loop()是个死循环
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

我们已经知道第一句:Looper.prepareMainLooper()是创建了Looper,并且创建了MessageQueue,而我们刚刚的handler.sendMessage(message)也在这个messageQueue里面 添加了message,现在就差取出来message使用了,肯定在Looper.loop()里面,好,我们看看,这里只列出部分关键代码:

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    boolean slowDeliveryDetected = false;

    for (;;) { //这里果然是个死循环,否则上面就会抛出异常了
        Message msg = queue.next(); //从messageQueue中取出message,终于找到了,在这里取出消息
        if (msg == null) {
            return; //没有消息就返回,就退出了循环,等等!那不就意味着上面那个异常会发生吗,app就crash了,除非msg==null不会发生,等会看看这个queue.next()会不会返回null
        }
        try {
            //我们前面说过msg.target就是handler.sendMessage(msg)的这个handler,那就是调用了这个handler的dispatchMessage(msg),好我们看看
            msg.target.dispatchMessage(msg); 
        } catch (Exception exception) {
            throw exception;
        } finally {
            ***省略其他***
        }
        msg.recycleUnchecked(); //回收
    }
}

Handler.dispatchMessage()如下:

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); //嗯,如果message有callback就直接调用
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) { //message没有callback就调用自己的mCallback了,也就是我们第一步的第三个参数
                return;
            }
        }
        handleMessage(msg); //最后才调用我们构造函数重载的handlerMessage(msg)方法,
    }
}

上面逻辑很简单,这里总结一下 1 如果msg有callback,那么只调用这个 2 如果msg没有callback,先调用自己的mCallback(如果有的话),如果mCallback的handleMessage(msg)返回true,就结束,否则调用Handler自己的handleMessage(msg) 分析完事,那么queue.next()会不会返回null呢,我们看看:

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null; //这里有返回null!!! 卧槽,什么情况下发生?
    }
    int nextPollTimeoutMillis = 0; //记录下一条消息还有多长时间执行
    for (;;) { //这里又是个死循环
        ...省略部分代码...

        //这个可以理解为java的wait(),表示让native层wait(nextPollTimeoutMillis)时间,嗯,等待这么长时间
        //还记得之前MessageQueue.enqueueMessage(msg)吗,里面的nativeWake(mPtr)就等价于noticy(),用来唤醒
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis(); //记录当前时间
            Message prevMsg = null;
            Message msg = mMessages; //取出第一条消息,也就是队头消息

            //这个if不用看了,系统使用的,以后再讲
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }

            //我们直接看这里
            if (msg != null) { //msg!=null,也就是队头消息不为null,也就是队列非空
                if (now < msg.when) {
                    //跑到这里表示当前时间小于第一条消息要执行的时间,也就是还没到执行的时候,msg是第一条消息,第一条消息还不到执行的时候,
                    //后面的消息都不用看了,因为前面分析过他们是按照执行时间从小到大排序的

                    //计算下一次执行还要多久,用msg.when - now算出来要等多久再取消息(这就是Handler能延时的原理),Integer.MAX_VALUE是为了防止应用层恶意传参数
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); 
                } else {
                    //跑到这里说明now >= msg.when,也就是说有消息要处理了
                    mBlocked = false; //那就解除阻塞
                    if (prevMsg != null) {
                        //这里不用看了,系统使用的,直接看else
                        prevMsg.next = msg.next;
                    } else {
                        //直接看这里,队头指向下一个消息
                        mMessages = msg.next;
                    }
                    msg.next = null; //这个msg的next赋值为null,那就是从队头删除了
                    msg.markInUse(); //标记为已使用
                    return msg; //返回,嗯,这个时候就回到looper.loop()里面了,去使用这个msg的targert.dispatchMessage(msg)了
                }
            } else {
                //msg==null,那就是队列为null,直接赋值为-1,表示无限等待,那就只能等待enqueueMessage(msg)里面的nativeWake(mPtr)来唤醒了,
                //也就是有消息才唤醒了
                nextPollTimeoutMillis = -1;
            }
            mBlocked = true; //跑到这里,表示没有return msg;那么肯定是阻塞了,标记为true,好,现在去看FLAG1
            ...省略部分代码...
        }
        ...省略处理空闲消息的代码...
    }
}

FLAG3,好现在我们从FLAG2回来了,我们总结一下: Looper.loop()是个死循环,从Message.next()获取msg,然后Message.next()也是死循环,循环取出当前队头的message,如果到了msg的执行时间就返回,并且使用mBlocked = false 标记为非阻塞,如果不到执行时间就先计算还有多久执行:int delay = msg.when - now, 然后调用nativePollOnce(delay)等待,等待结束就会执行,如果没有消息就无限等待,等待过程中会 把mBlocked = true标记为阻塞,等到有新消息入队,也就是enqueueMessage(msg,when)的时候,会判断此时是不是有消息需要执行: if (p == null || when == 0 || when < p.when), 如果有消息需要执行,就判断是否阻塞,如果阻塞,就唤醒(needWake = mBlocked),否则不唤醒。 本节分析到此结束,代码中标记的问题,后续会讲解。

问题1: MessageQueue不会无限休眠吗? 所以要么是限时等待,时间到了就会自动醒来,要么是无限等待,无限等待只发生在MessageQueue为空的情况下,而在此情况下,如果有新消息入队,一定满足p == null,也就是一定会唤醒,所以不存在 无限休眠的状态。

问题2: Handler怎么实现延时机制的? 仔细看注释的小伙伴已经知道了,Handler是获取当前时间,然后加上delay的时间,保存到message.when中,然后在looper.loop()中获取当前时间跟这个when对比,如果小于说明需要立刻执行,否则就做个减法: int waitTime = message.when - now;然后调用cpu等待waitTime再执行,从而达到了延时的效果

问题3: 里面有一些异步消息是什么情况下使用的? 这个是用于屏幕渲染时候的VSYNC机制的,可以这么说: VSYNC通过handler来实现同步,VSYNC的消息都是异步的,会优先被处理,后面章节会仔细讲解