Android Handler 机制(一):Handler 运行机制完整梳理

Handler 运行机制完整梳理

对于Android开发者而言,Handler运行机制是基础且核心的知识点,属于老生常谈但必须吃透的内容。

先简单梳理核心角色的职责,帮大家快速回顾:

  • Handler:负责发送消息,以及接收Looper回传的消息并处理;

  • Looper:负责接收Handler发送的消息,循环取出消息并在合适的时机回传给对应的Handler;

  • MessageQueue:作为消息的存储容器,采用队列结构管理Message,保证消息的有序性。

本文将结合源码,详细、完整地梳理Handler的整套运行机制,帮大家理清各角色间的关联与执行流程。

一、ActivityThread类和APP的启动过程

我们先从ActivityThread和App的启动过程讲起,因为Handler、Looper的创建与初始化,正是在这个阶段完成的,这是理解Handler运行机制的前提。

ActivityThread就是我们常说的主线程(也叫UI线程),一个APP的真正入口并非Activity的onCreate方法,而是ActivityThread的main方法,MainLooper也正是在这个main方法中被创建的。


    //ActivityThread的main方法

    public static void main(String[] args) {

        ...

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();

        //在attach方法中会完成Application对象的初始化,然后调用Application的onCreate()方法

        thread.attach(false);



        if (sMainThreadHandler == null) {

            sMainThreadHandler = thread.getHandler();

        }

 ...

        Looper.loop();

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

    }

主线程的Handler作为ActivityThread的成员变量,会在ActivityThread的main方法执行、ActivityThread实例创建时完成初始化;而MessageQueue则会在Looper创建时,作为Looper的成员变量同步完成初始化,三者的初始化顺序紧密关联。

二、Handler创建Message并发送给Looper

当我们在代码中创建Message,并通过Handler发送消息时,其内部会经过一系列调用,核心流程如下:


public final boolean sendMessageDelayed(Message msg, long delayMillis) {

        if (delayMillis < 0) {

            delayMillis = 0;

        }

        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

无论我们调用Handler的哪一种发送消息方法(如sendMessage、sendMessageDelayed等),最终都会调用到MessageQueue的enqueueMessage方法,该方法是消息入队的核心处理逻辑。以下是MessageQueue.enqueueMessage方法的核心代码:


    boolean enqueueMessage(Message msg, long when) {

        ...synchronized (this) {

            ...

            msg.markInUse();

            msg.when = when;

            Message p = mMessages;

            boolean needWake;

            if (p == null || when == 0 || when < p.when) {

                // 队列为空、消息立即执行或消息执行时间更早,插入队列头部

                msg.next = p;

                mMessages = msg;

                needWake = mBlocked;

            } else {

                // 消息需插入队列中间,通常无需唤醒消息队列,除非队列头部有屏障且当前消息是最早的异步消息

                needWake = mBlocked && p.target == null && msg.isAsynchronous();

                Message prev;

                // 循环找到消息对应的插入位置(按执行时间when排序)

                for (;;) {

                    prev = p;

                    p = p.next;

                    if (p == null || when < p.when) {

                        break;

                    }

                    if (needWake && p.isAsynchronous()) {

                        needWake = false;

                    }

                }

                msg.next = p; // 保持队列链表结构

                prev.next = msg;

            }



            // 若需要唤醒,调用native方法唤醒消息队列(避免阻塞)

            if (needWake) {

                nativeWake(mPtr);

            }

        }

        return true;

    }

这段代码的核心作用的是:将待发送的Message按照执行时间(when)插入到MessageQueue的合适位置,保证消息按执行顺序排列;同时通过needWake标识判断是否需要调用底层方法,唤醒处于阻塞状态的消息队列。

结合上述代码,Handler发送消息的整体逻辑如下所示:


+-------+     +------------+   +------------------+   +--------------+                      
|Handler|     |MessageQueue|   |NativeMessageQueue|   |Looper(Native)|                      
+--+----+     +-----+------+   +---------+--------+   +-------+------+                      
   |                |                    |                    |                              
   |                |                    |                    |                              
sendMessage()|                |                    |                    |                              
+----------> |                |                    |                    |                              
   |                |                    |                    |                              
   |enqueueMessage()|                    |                    |                              
   +--------------> |                    |                    |                              
   |                |                    |                    |                              
   |                |                    |                    |                                            
   |                |  nativeWake()      |                    |                              
   |                |    wake()          |                    |                              
   |                +------------------> |                    |                              
   |                |                    |                    |                              
   |                |                    |    wake()          |                              
   |                |                    +------------------> |                              
   |                |                    |                    |                              
   |                |                    |                    |                              
   |                |                    |                    |write(mWakeWritePipeFd, "W", 1)
   |                |                    |                    |                                 
   |                |                    |                    |                                                            
   +                +                    +                    +                              

三、Looper循环处理MessageQueue的Message

Looper的核心功能是循环从MessageQueue中取出消息并分发,其核心方法就是Looper.loop(),所有消息的循环处理都围绕这个方法展开。

Looper.loop()方法的核心代码如下:


/**

 * Run the message queue in this thread. Be sure to call

 * {@link #quit()} to end the loop.

 */

public static void loop() {

    final Looper me = myLooper();

    ...

    // 死循环,持续从MessageQueue中取消息

    for (;;) {

        // 取出消息,若队列中无消息则会阻塞

        Message msg = queue.next(); // might block

        if (msg == null) {

            // 消息队列退出时返回null,循环终止

            return;

        }



        // 日志打印(非核心逻辑,可忽略)

        final Printer logging = me.mLogging;

        if (logging != null) {

            logging.println(">>>> Dispatching to " + msg.target + " " +

                    msg.callback + ": " + msg.what);

        }

        ...

    }

}

从上述代码可以看出,Looper.loop()是一个死循环,其核心是调用MessageQueue的next()方法取出消息,而next()方法则负责从队列中获取下一条可执行的消息,其核心代码如下:


    Message next() {

        // 若消息循环已退出并释放资源,直接返回null

        final long ptr = mPtr;

        if (ptr == 0) {

            return null;

        }



        int pendingIdleHandlerCount = -1; // 仅在第一次循环时为-1

        int nextPollTimeoutMillis = 0;

        for (;;) {

            if (nextPollTimeoutMillis != 0) {

 Binder.flushPendingCommands();

            }



 // 调用native方法,根据超时时间阻塞队列

            nativePollOnce(ptr, nextPollTimeoutMillis);



            synchronized (this) {

                final long now = SystemClock.uptimeMillis();

                Message prevMsg = null;

                Message msg = mMessages;

                // 若队列头部有消息屏障,查找下一条异步消息

                if (msg != null && msg.target == null) {

                    do {

                        prevMsg = msg;

                        msg = msg.next;

                    } while (msg != null && !msg.isAsynchronous());

                }

                if (msg != null) {

                    // 消息未到执行时间,设置下次唤醒超时时间

                    if (now < msg.when) {

 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

                    } else {

                        // 取出消息,更新队列结构,标记消息为已使用并返回

                        mBlocked = false;

                        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;

                    }

 } else {

                    // 队列中无消息,设置超时时间为-1,进入无限阻塞

 nextPollTimeoutMillis = -1;

                }



                // 若消息队列正在退出,释放资源并返回null

                if (mQuitting) {

                    dispose();

                    return null;

                }



                // 处理空闲Handler(非核心逻辑,简要执行)

                if (pendingIdleHandlerCount < 0

                        && (mMessages == null || now < mMessages.when)) {

                    pendingIdleHandlerCount = mIdleHandlers.size();

                }

                if (pendingIdleHandlerCount <= 0) {

                    // 无空闲Handler,标记队列阻塞,继续循环

                    mBlocked = true;

                    continue;

                }



                if (mPendingIdleHandlers == null) {

                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];

                }

                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

            }



            // 执行空闲Handler的逻辑

            for (int i = 0; i < pendingIdleHandlerCount; i++) {

                final IdleHandler idler = mPendingIdleHandlers[i];

                mPendingIdleHandlers[i] = null; // 释放引用



                boolean keep = false;

                try {

 keep = idler.queueIdle();

                } catch (Throwable t) {

                    Log.wtf(TAG, "IdleHandler threw exception", t);

                }



                if (!keep) {

                    synchronized (this) {

                        mIdleHandlers.remove(idler);

                    }

                }

            }



            // 重置空闲Handler计数,避免重复执行

            pendingIdleHandlerCount = 0;

            // 执行空闲Handler期间可能有新消息,重置超时时间重新检查

            nextPollTimeoutMillis = 0;

        }

    }

这段代码的核心逻辑的是:不断从MessageQueue中取出可执行的消息;若队列中无消息,或消息未到执行时间,则通过nativePollOnce方法让消息队列进入阻塞状态,避免占用CPU资源;当有新消息进入或消息到执行时间时,队列会被唤醒,继续取出消息。其中,当nextPollTimeoutMillis设为-1时,消息队列会进入无限阻塞状态,直到被唤醒。

结合上述代码,Looper循环处理消息的整体逻辑如下所示:


+------+    +------------+  +------------------+  +--------------+                   
|Looper|    |MessageQueue|  |NativeMessageQueue|  |Looper(Native)|                   
+--+---+    +------+-----+  +---------+--------+  +-------+------+                   
   |               |                  |                   |                                                            
+-------------------------------------------------------------------------------+               
|[msg loop]  |   next()      |                  |                   |           |               
|            +------------>  |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               | nativePollOnce() |                   |           |               
|            |               |    pollOnce()    |                   |           |               
|            |               +----------------> |                   |           |               
|            |               |                  |                   |           |              
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |     pollOnce()    |           |               
|            |               |                  +-----------------> |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   | epoll_wait()              |            |               |                  |                   +--------+  |               
|            |               |                  |                   |        |  |               
|            |               |                  |                   |        |  |               
|            |               |                  |                   | <------+  |               
|            |               |                  |                   | awoken()  |               
|            +               +                  +                   +           |                 
+-------------------------------------------------------------------------------+              

四、总结

1. 相关知识点

理解Handler运行机制,还需要掌握与之相关的三个关键知识点:HandlerThread、ThreadLocal、Linux Epoll 机制。

HandlerThread:

HandlerThread相比普通Thread的最大优势,在于其内部引入了MessageQueue消息队列机制,能够实现多任务的队列管理。HandlerThread背后仅对应一个线程,因此所有任务都会串行依次执行,相比并行任务更安全,不会出现多线程并发安全问题。此外,HandlerThread创建的线程会持续存活,其内部的Looper会不断循环检查MessageQueue,执行消息处理逻辑——这一点与普通Thread、AsyncTask不同,线程的重用可以避免线程及相关对象的频繁创建与销毁,提升性能。若要退出该线程,调用getLooper().quit()即可,其原理是修改消息循环中的标志位,终止Looper的死循环,让线程正常执行完毕。

注意:HandlerThread属于子线程,若需要通过它更新UI界面,仍需使用主线程的Looper,否则会抛出UI线程更新异常。

2. 推荐文章:

  1. Android Handler机制 - MessageQueue如何处理消息:https://blog.csdn.net/lovelease/article/details/81988696

  2. ActivityThread的理解和APP的启动过程:https://blog.csdn.net/hzwailll/article/details/85339714

posted @ 2020-05-10 12:51  灰色飘零  阅读(4109)  评论(0)    收藏  举报