Handler 源码解析(一)—— Handler 的工作流程

1,521 阅读10分钟

Handler 源码解析系列文章:

  1. Handler 源码解析(一)—— Handler 的工作流程
  2. Handler 源码解析(二)—— 正确创建 Handler 对象
  3. Handler 源码解析(三)—— Handler 内存泄漏
  4. ...

1. Handler 机制及其使用场景

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue

通过 Handler 机制,你可以发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。

那么通过 Handler 机制发送 Message 或 Runnable 对象主要是为了做些什么呢?总的来说, Handler 机制两个主要的使用场景:

  1. 执行延迟任务:发送延迟消息或可运行对象,使其在将来的某个时间点执行。

    Eg:在某些场景下,需要延时或定时执行某些操作,如定时刷新数据、延时提醒等。

  2. 线程间通信: 将要在不同于当前线程的另一个线程上执行的操作加入队列。

    Eg:子线程中执行一些耗时操作,若子线程需进行更新 UI 操作,而此操作必须在 UI 线程执行,可以使用 Handler 将更新操作传递给 UI 线程。

2. Handler 机制中的组件

2.1 四大组件

1. Handler

消息的发送者和处理者,通过 Handler 提供的方法,可以发送和处理消息或 Runnable 对象。

2. Message

消息对象,消息载体,可以携带少量信息。Handler 对象最终发送的消息就是 Message 对象,Message 对象中含有该 Handler 对象的引用。线程间内存共享。

3. MessageQueue

消息队列,Handler 发送的消息全部存储在该队列中。每个线程中只会存在一个 MessageQueue 对象。

4. Looper

负责维护一个消息循环(Message Loop),从 MessageQueue 中循环读取消息,并将他们分配给对应的 Handler 进行处理。每个线程只有一个 Looper 对象。

2.2 四大组件之间的关系

Handler 机制工作流程:

  1. 应用程序进程创建后,相关环境准备,创建消息队列 MessageQueue,创建 Looper 实例。通过 Looper 实例启动一个无限循环,不断从 MessageQueue 中取消息。
  2. 发送消息,可以通过 Handler 对象发送消息,该消息会进入到 MessageQueue 中,MessageQueue 中的消息是按照执行时间进行排列,需要先执行的消息会先被取出。
  3. 无限循环会不断从 MessageQueue 中取消息。获取消息的方式是阻塞式的,当队列为空时,线程进入休眠状态(基于Linux的epoll机制),避免CPU资源浪费。
  4. 上文说过,Message 对象中含有 Handler 对象的引用。从 MessageQueue 获取到消息后会将消息分发给对应的Handler处理。

一个线程可以有多个 Handler 实例。每个 Handler 负责不同类型的消息或处理不同的任务,提供了更灵活的消息处理机制。

一个线程只有一个 Looper、一个 MessageQueue。同一线程中的每一个 Handler 实例都与同一个 Looper 有关。一个线程只有一个 Looper 的设计是为了保持消息队列的一致性,以确保消息的有序处理,一个线程通常只有一个消息队列是为了避免混淆和提供明确的线程间通信机制。

Handler 机制工作流程模型:

image.png

3. Handler 机制的工作流程

结合上文所讲,下面详细介绍一下 Android Handler 机制的工作流程。

3.1 环境初始化 —— Looper 和 MessageQueue

应用进程创建完毕,会进入到 ActivityThread 的 main 方法中:

public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
        
    public static void main(String[] args) {
        ...
        // 设置主线程 Looper,一个线程一个 Looper
        Looper.prepareMainLooper();
        ...
    }
}

调用了 Looper 的 prepareMainLooper 方法,创建 Looper 对象:

public static void prepareMainLooper() {
    // 创建主线程 Looper 实例并存入 ThreadLocal.ThreadLocalMap
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // 从 ThreadLocal.ThreadLocalMap 中取出主线程 Looper 实例,并赋值给 sMainLooper
        sMainLooper = myLooper();
    }
}

实际上是通过 prepare 方法创建 Looper 对象

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");
    }
    // Thread 中有 ThreadLocal.ThreadLocalMap 的引用
    // set 方法会将存入 ThreadLocal.ThreadLocalMap 中
    sThreadLocal.set(new Looper(quitAllowed));
}

ThreadLocal 对象用于存储线程私有数据。ThreadLocal 是一个线程本地存储机制,每个线程都有自己的局部变量副本,这些副本独立于其他线程,每个线程可以拥有自己的状态,而不会影响其他线程。这避免了多线程环境下的数据竞争和同步问题,提高了程序的性能和安全性。

主线程中的 sThreadLocal 对象开始是空的,所以通过 new 创建一个 Looper 对象,并通过 set 方法存入到 sThreadLocal 中。

该 Looper 对象就是主线程 Looper 对象,会赋值给 sMainLooper,sMainLooper 是 Looper 类中的静态字段。

在创建 Looper 对象时,其构造方法中会创建 MessageQueue 对象

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

Looper 对象持有了 MessageQueue 对象的引用。可以看到该构造方法是私有的,只能通过 Looper 的 prepare 方法进行创建,此种构造方法为单例模式。

这样,主线程中创建了一个 Looper 对象,并且该 Looper 对象对应到一个 MessageQueue 对象。

3.2 开启消息循环

创建完 Looper 对象和 MessageQueue 对象后,通过 Looper 的 loop 方法开启了主线程的 Looper 循环:

public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
        
    public static void main(String[] args) {
        ...
        // 设置主线程 Looper,一个线程一个 Looper
        Looper.prepareMainLooper();
        ...
        // 开启主线程 Looper 循环
        Looper.loop();
    }
}

来看一下 Looper 中的 loop() 方法:

public static void loop() {
    // 获取当先线程的 Looper。 ActivityThread 的 main 方法中获取的是主线程 sMainLooper
    final Looper me = myLooper();
    if (me == null) {
        // 如果当前线程没有 Looper,抛出异常。所以子线程必须调用 Looper.prepare() 去创建当前线程的 Looper。
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }

    // 当前线程 Looper 已经进入循环状态
    me.mInLoop = true;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // Allow overriding a threshold with a system prop. e.g.
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride = getThresholdOverride();

    me.mSlowDeliveryDetected = false;

    // 进入 Looper 死循环
    for (;;) {
        // me:当前线程的 Looper
        // ident:当前线程的标识
        // thresholdOverride:系统属性值,如果大于 0,则表示需要打印日志
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

主要看一下第28行的死循环,不断调用 loopOnce() 方法,接下来看一下 Looper 中的 loopOnce() 方法:

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next(); // might block
    
    if (msg == null) {//主线程不会使msg为空
        // No message indicates that the message queue is quitting.
        return false;
    }
    
    ...
    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 {
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
    ...
}

该方法中第3行调用的 MessageQueue 的 next() 方法:

next() 方法为出队方法。如果当前 MessageQueue 中存在 mMessages (即待处理消息),就将这个消息出队,然后让下一条消息成为 mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队:

Message next() {
    ...
    for (;;) { // 进入死循环,有合适的消息就返回,无消息就阻塞
        ...
        // 调用Native方法阻塞线程,释放CPU资源直到新消息到达或超时
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {// 获取下一个消息
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;// 用于链表操作的前驱节点
            Message msg = mMessages;// 当前链表的头结点
            // 处理同步屏障
            ...
            if (msg != null) {// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
                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 { // 消息是正常的、可以立即处理的
                    // Got a message.
                    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 {// 无消息可处理,进入阻塞状态
                // No more messages.
                // 消息链表里没有消息了
                nextPollTimeoutMillis = -1;// 永久阻塞直到被唤醒
            }
            ...
    }
}

next() 返回的消息对象会赋给 msg:Message msg = me.mQueue.next();

3.3 通过 Handler 对象发送消息

使用 Handler 发送接收消息,首先创建 Handler 对象(可参考文章:Handler 源码解析(二)—— 正确创建 Handler 对象):

Handler handler = new Handler(getMainLooper(), new Handler.Callback() {  
    @Override  
    public boolean handleMessage(@NonNull Message msg) {  
        if ( msg.what == 1)  
            Log.i("MainActivity", "msg: " + (String) msg.obj);  
        return false;  
    }  
});  

new Thread(new Runnable() {  
    @Override  
    public void run() {  
        Message msg = Message.obtain();  
        msg.what = 1;  
        msg.obj = "Hello, Handler";  

        handler.sendMessage(msg);  
    }  
}).start();  

Handler 类中提供了很多调度消息的方法:

image.png

send 开头的方法,除了 sendMessageAtFrontOfQueue() 方法之外,其他方法最终都会调用到 sendMessageAtTime()方法: image.png 若没有调用 sendMessageDelayed() 方法,延迟时间就为0。

image.png

post 开头的方法,发送的内容为 Runnable 对象,最终也会调用到 send 开头方法,发送 message。

post(Runnable r) 为例:

public final boolean post(@NonNull Runnable r) {  
    return sendMessageDelayed(getPostMessage(r), 0);  
}

都会通过 getPostMessage() 方法创建一个对应的 Message 对象,然后再调用 send 方法:

private static Message getPostMessage(Runnable r) {  
    Message m = Message.obtain();  
    m.callback = r;  
    return m;  
}

3.4 将消息对象添加到 MessageQueue 中

接下来将参数传入 Handler 的入队方法 enqueueMessage() 中:

// Handler.java
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);
}

第三行的 msg.target 中的 target 是 Message 类中的一个 Handler 对象。msg.target 在 Message 对象中记录了该 msg 是由哪一个 Handler 对象发出的,等到分发消息的时候,通过 target 再分发回对应的 Handler 对象。

MessageQueue提供了入队和出队的方法,将所有收到的消息以队列的形式排列,接着通过 queue 调用 MessageQueue 中的 enqueueMessage() 方法:

// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    // 发送 msg 的 handler 对象是否为 null
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    synchronized (this) {
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        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;
            for (;;) {// 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
                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;
}

MessageQueue 使用 mMessages 保存当前待处理消息。该入队方法将所有消息按照发送消息的时间进行排序。

根据时间的顺序调用 msg.next,从而为每一个消息指定它的下一个消息是什么。这时会把 mMessages 赋值为新入队的这条消息,然后将这条消息的 next 指定为刚才的 mMessages,这样也就完成了添加消息到队列头部的操作。

image.png

3.5 主线程取消息,处理消息

MessageQueue 中加入消息后,主线程 Looper 循环就会取消息, next() 返回的消息对象会赋给 msg:Message msg = me.mQueue.next();

回过头接着看 loopOnce() 方法,第12行每当有一个消息出队,就将它传递到 msg.targetdispatchMessage() 方法中。接下来看一下 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);
    }
}

第2行判断 Message 是否设置了 callback,如果设置了则调用,否则继续执行。

Message 可以通过 setCallback 方法设置 callback,但并不推荐使用:

/** @hide */  
@UnsupportedAppUsage  
public Message setCallback(Runnable r) {  
    callback = r;  
    return this;  
}

第5行,如果 mCallback 不为空,则调用 mCallback 的 handleMessage() 方法,mCallback 可以在创建 Handler 对象时设置。否则直接调用 Handler 的 handleMessage() 方法,并将消息对象作为参数传递过去。

image.png