Skip to content

Handler实现原理

ZhangPan edited this page Jul 20, 2025 · 7 revisions

Handler 机制原理概述

Android Handler 机制是一个标准的 “生产者-消费者” 模式。Handler 是生产者,Looper 是消费者。而 MessageQueue 则为“生产者-消费者”的缓冲队列。Handler 通过 postXxx() 方法发送出 Message,并将 Message 存入 MessageQueue 中,Message 通过 target 持有了Handler。在插入MessapgeQueue之后通过 nativeWake 方法唤醒消费者。

Looper 作为消费者,它的职责是通过缓冲队列 MessageQueue 的 next 方法不断的取出 Message 进行消费,并通过 Message.target 回调给 Handler,当 MessageQueue 为空时,next 方法会被阻塞,从而让 Android 进程不被结束运行。

MessapgeQueue 作为缓冲队列,它的职责是存放 Looper 来不及处理的消息。在 Android 的消息机制中,MessapgeQueue 是一个具有优先级的缓冲队列。在插入消息时,MessapgeQueue 会根据消息的优先级对消息进行排序,优先级高的先执行,优先级低的后执行。消息的优先级可以分为三个等级。

  • 异步消息,异步消息拥有最高的优先级。通过 setAsynchronous(true) 可以将消息变成异步消息。异步消息的执行依赖了 MessageQueue#postSyncBarrier() 方法。postSyncBarrier 方法会在 MessageQueue 中插入一个 target 为空的 Message。Looper 在读取到这个消息后就会停止执行普通消息,通过遍历 MessageQueue 找到异步消息并进行消费。
  • 普通消息,普通消息的优先级低于异步消息。
  • 延迟消息,延迟消息是给Message设置了一个执行时间,MessageQueue 会根据延迟时间,对这个消息就行排序,延迟越长的消息,就越会被放置在MessageQueue的尾部。

除了以上几种优先级的消息外,Handler 机制还提供了一种更低的优先级“消息”: IdleHandler。实际上,IdleHandler 并不是存储在 MessageQueue 中的 Message。而是存储在 MessageQueue 的一个 List 集合中,只有当 MessageQueue 中没有消息时,才会遍历这个集合去执行 IdleHandler。因此,可以将它认为时一种最低优先级的消息。

Handler 机制源码分析

(1)Handler

通过 Handler 的 sendXXX 或者 postXXX 来发送一个消息,这里要注意 post(Runnable r) 方法也会将 Runnable 包装成一个 Message,代码如下:

    public final boolean post(Runnable r){
    	return  sendMessageDelayed(getPostMessage(r), 0);
    }
    public final boolean postDelayed(Runnable r, long delayMillis){
    	return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

从代码中可以看到将Runnable赋值给了Message.callback了。最终sendXXX和postXXX都会调用到sendMessageAtTime,代码如下:

 public boolean sendMessageAtTime(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);
    }

在这个方法中最终调用了 enqueueMessage 方法,这里注意将this赋值给了Message.target,而此处 this 就是Handler。

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

enqueueMessage 方法最终调用了 MessageQueue 的 enqueueMessage 方法,将消息放入队列。

(2)MessageQueue

MessageQueue是一个优先级队列,核心方法是 enqueueMessage和 next方法,也就是将插入队列,将消息取出队列的操作。 之所以说MessageQueue是一个优先级队列是因为enqueueMessage方法中会根据Message的执行时间来对消息插入,这样越晚执行的消息会被插入到队列的后边。

boolean enqueueMessage(Message msg, long when) {

    synchronized (this) {

        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.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 这种情况下说明要插入的消息是延迟消息,遍历链表找到合适的插入位置
            Message prev;
            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;
        }

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

而next方法是一个死循环,如果队列中有消息,则next方法会将Message移除队列并返回该Message,如果队列中没有消息该方法则会处于阻塞状态。

Message next() {

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {

        // 阻塞线程
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
        
            // 消息队列头
            Message msg = mMessages;
            // 如果 msg 不为 null,并且 msg.target 为 null,则执行if中的逻辑
            if (msg != null && msg.target == null) {
                // 走到这里说明读取到了同步屏障消息
                // 通过 do...while 循环遍历message链表,找到异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            
            if (msg != null) {
              
                if (now < msg.when) { // 根据delay的时间判断该Message是否可以执行
                    // 未到执行时间则走到下一次循环调用nativePollOnce阻塞该方法
                    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;
                    msg.markInUse();
                    // 将消息返回
                    return msg;
                }
            } else {
                // 消息队列为空,阻塞时间设置于为-1,表示一直阻塞
                nextPollTimeoutMillis = -1;
            }
        }
        
        // 如果第一次调用next方法,pendingIdleHandlerCount 一定小于0
            // mMessage == null 说明消息队列中没有消息
            // now < mMessages.when 说明有延迟消息,但是还没有到执行的时间
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                // 获取IdleHandler的个数    
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            // 说明没有 IdleHandler
            if (pendingIdleHandlerCount <= 0) {
                mBlocked = true;
                跳过下面的逻辑继续执行for循环
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            // 将mIdleHandlers中的数据复制到mPendingIdleHandlers数组中
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 遍历 mPendingIdleHandlers 数组
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            // 取出遍历到的IdleHandler
            final IdleHandler idler = mPendingIdleHandlers[i];
            // 将以取出的位置设置为null
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                // 执行Idler.queueIdle方法
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            // 这里可以看出 queueIdle 如果返回false,则会将这个IdleHandler从集合中移除,下次就不会再执行了。
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
    }
}

(3)Looper Looper可以理解为一个消息泵,Looper的核心方法是loop。注意loop方法的第一行会首先通过myLooper来得到当前线程的Looper,接着拿到Looper中的MessageQueue,然后开启一个死循环,它会不断的通过MessageQueue的next方法将消息取出来,并执行。代码如下:

public static void loop() {
        final Looper me = myLooper();// 这里要特别注意,是从ThreadLocal中拿到当前线程的Looper。
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        for (;;) {
            //从 MessageQueue 中取消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
			//通过 Handler 分发消息
            msg.target.dispatchMessage(msg);
            //回收消息
            msg.recycleUnchecked();
        }
    }

可以看到在取出Message后则会调用Message.target调用dispatchMessage方法,这里target就是Handler,它是在Handler的enqueueMessage时赋值的。紧接着将Message进行了回收。 接下来再回到Handler看dispatchMessage,代码如下:

	public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //通过 handler.postXxx 形式传入的 Runnable
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //以 Handler(Handler.Callback) 写法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //以 Handler(){} 内存泄露写法
            handleMessage(msg);
        }
    }

可以看到,这里最终会调用到我们自己的实现方法。

公众号:玩转安卓Dev

Java基础

面向对象与Java基础知识

Java集合框架

JVM

多线程与并发

设计模式

Kotlin

Android

项目相关问题

Android基础知识

Android消息机制

Android Binder

View事件分发机制

Android屏幕刷新机制

View的绘制流程

Activity启动

Framework

性能优化

Jetpack&系统View

第三方框架实现原理

计算机网络

算法

Clone this wiki locally