Handler消息机制详解,只此一篇,以后不会忘了 - Xiasm/Java-Android-Learn GitHub Wiki

Android消息机制详解,只此一篇,以后不会忘了

Handler消息机制是Android中很重要的一个知识点,之前网上有很多同学分享了大量的博客介绍handler机制,那么为什么我要再写一篇呢?因为我看过的博客发现分析的干巴巴的,不容易记忆。所以呢,这次我花时间做了一张消息机制的时序图,为的就是看图给右脑记忆,从此不在忘记!!!

好了,废话不多说,先看时序图,下面我会根据图讲解串通:


从哪看起呢?大家分析Handler消息机制的时候要么从sendMessage()分析起来,要么从Looper.prepare()开始,我喜欢从后者开始,因为sendMessage()执行的前提就是Looper调用了loop()方法,不然也收不到消息。我们就先从Looper说起,因为Looper执行loop方法之后才能正常接收到消息并执行后续操作。

主线程Looper初始化

我们先看图中的Looper对象,在Looper对象的第一个控制焦点,即上图中 “ActivityThread创建,初始化Looper,新建MessageQueue,即Queue” 焦点,由这个焦点可看出我们谈论的是主线程中的Looper,所以上图中的Looper对象就是主线程的。子线程的其实类似,就是Looper的初始化需要我们自己调用,我们先看主线程的。
主线程中的Looper什么时候创建的呢?因为我们代码中并没有为UI线程创建Looper,实际上创建应用程序的时候系统就已经为我们创建好了UI线程的Looper,看代码:

public static void main(String[] args) {
    // ...
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    // ...
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

这是ActivityThread的main()方法,可以认为就是应用程序创建的入口,main()方法里我们先调用了Looper.prepareMainLooper()方法,准备好Looper之后,最后调用了Looper.loop(),这时候主线程(即UI线程,后续不在解释)的Looper就开始了循环,也就意味着我们可以往主线程发消息了。

Looper.prepareMainLooper()内部还是调用了prepare()方法,往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));
}

我们看下Looper的构造方法:

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

构造方法里会创建一个MessageQueue对象,顾名思义,就是用来放Message的队列,Looper调用loop()方法之后开始循环,就会从这个MessageQueue里面取消息。有取必然有放,我们看完了Looper的初始化,Looper循环也运行起来了。所以接下来看发消息。

Handler发送消息

需要发送消息,那么就必须要创建Handler,所以我在图中加了一个Activity对象,并调用Handler的构造方法创建Handler对象,Handler的构造方法有个点需要注意:

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    //标注1
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    //标注2
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

创建Handler最终会调用到上面的构造方法,当然创建子线程接收消息的Handler除外。标注1处通过Looper.myLooper()获取到Looper对象,myLooper方法内部还是从ThreadLocal里面取的,所以拿到的当然是主线程的Looper了。标注2处拿到Looper的MessageQueue,只要拿到这个MessageQueue,我们在Handler里面就可以往主线程的MessageQueue里面放Message了。

好了,我们终于可以开始发消息了

看图中Thread对象的第一个焦点,这个Thread对象就是模拟工作线程,第一个焦点调用Message.obtain()方法获得一个msg,然后调用Handler的sendMessage()方法(这里不重要的点我就不在贴源码,因为图中生命线和焦点就是源码方法调用的时序,大家在看的时候可以对照源码),sendMessage()内部调用Handler的sendMessageDelayed()方法,到达下一个焦点,sendMessageDelayed()方法又调用了Handler内部的sendMessageAtTime()方法,sendMessageAtTime()方法接着调用了Handler本身的enqueueMessage()方法,enqueueMessage()我要贴下代码,因为这里有个精彩的地方:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

这里我们发送的msg把它的target属性指向了Handler自身,为什么要这样做呢,就是为了Looper接收到消息之后回调Handler的callback,后面我们把消息发到Looper的时候会细说,继续看,enqueueMessage()方法调用了queue.enqueueMessage(msg, uptimeMillis)方法,这个queue就是我们创建Handler时候的MessageQueue,我们看MessageQueue的enqueueMessage()方法:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        //...
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //标注1
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            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;
            //标注2
            for (;;) {
                prev = p;
                p = p.next;
                //标注3
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

代码挺长,实际上就是入队操作,这里有个mMessages,并且p指向了mMessage,我们可以把它理解为是待执行的message队列,该队列是按照when的时间排序的且第一个消息是最先执行。
我们看标注1处,有三个条件,如果mMessages对象为空,或者when为0也就是立刻执行,或者新消息的when时间比mMessages队列的when时间还要早,符合以上一个条件就把新的msg插到mMessages的前面 并把next指向它,也就是msg会插进上图中队列的最前面,等待loop的轮询。
如果上面的条件都不符合就进入else代码中,我们可以看到标注2处有个for的死循环遍历已有的message对象,其中标注3中有个if语句when < p.when when是新消息的执行时间,p.when是队列中message消息的执行时间,如果找到比新的message还要晚执行的消息,就执行 msg.next = p; prev.next = msg;也就是把插到该消息的前面,优先执行新的消息。好了,到这里我们发送的消息就到了队列里面,发送消息已经完结了,下面就等Looper循环到我们的消息就行了。

Looper循环

Looper调用loop()方法之后就是一个死循环,如下是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;

    //...

    for (;;) {
        //标注1
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        //...
        
        try {
            //标注2
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //...
    }
}

代码很长,我只贴出有用的,可以看到标注1处,会从queue里面取出msg,然后看标注2,会调用msg的target属性的dispatchMessage(msg)方法,首先这个target我们上面提了一下,就是在Handler的enqueueMessage()方法里我们把Handler自身赋值给了msg的target属性,那么loop循环里调用到标注2处就又回到了Handler的dispatchMessage(msg)方法。

分发消息

dispatchMessage(msg)就是分发消息的处理,我们看源码:

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

如果msg本身的callback不为空,就调用msg自身的回调方法,否则进入else里面
else里面先判断mCallback是否为空,这个mCallback在Handler构造方法里可以传入,如果mCallback不为空就调用mCallback回调,否则调用handleMessage(msg)方法。到这个回调,消息已经接收到了,下面就是我们自己的业务逻辑处理了。

我写这篇博客的目的其实还是上面的流程图,如果大家把流程图弄懂了,以后自然就记住了。

⚠️ **GitHub.com Fallback** ⚠️