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

1,455 阅读6分钟

Handler 源码解析系列文章:

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

1. Handler 概述

1.1 Handler 机制

Android Handler机制用于在不同线程之间进行通信,进行异步消息处理。

1.2 Handler 机制的组件

  1. Handler:用于发送和处理消息或 Runnable 对象。
  2. Message:消息对象,在线程之间传递消息,可以携带少量信息,可以包含数据和在指定时间运行任务的指令。线程间内存共享。
  3. MessageQueue:消息队列,用于存储由 Handler 发送的消息。每个线程中只会存在一个 MessageQueue 对象。
  4. Looper:负责维护一个消息循环(Message Loop),从 MessageQueue 中循环读取消息,并将他们分配给对应的 Handler 进行处理。每个线程只有一个 Looper 对象。

image.png

1.3 Handler 的使用场景

  1. 在子线程更新 UI:当后台线程完成一些任务后,需要在UI线程更新UI元素时,可以使用Handler将更新操作传递给UI线程。
  2. 执行延迟任务:需要在未来某个时间点或延迟一段时间后执行任务时,可以使用Handler的postDelayed()方法来实现。
  3. 定时任务:需要定期执行某些任务,例如定时刷新数据或执行轮询操作时,Handler可以通过postDelayed()方法实现定时任务。
  4. 线程间通信:在不同的线程之间传递消息或执行特定的任务,Handler可以作为线程间通信的桥梁。
  5. 处理消息队列:需要对消息进行排队处理,根据优先级执行任务时,可以使用Handler的消息机制来实现。

2. 一个线程有几个 Handler、几个 Looper、几个 MessageQueue?

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

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

从代码的角度看一下 Looper 对象的创建,Looper 的构造方法是私有的:

// 本文代码基于Android api 33
private Looper(boolean quitAllowed) {  
    mQueue = new MessageQueue(quitAllowed);  
    mThread = Thread.currentThread();  
}

可以通过 Looper 的 prepare() 方法创建对象,为单例模式

public static void prepare() {  
    prepare(true);  
}  
  
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));  
}

3. Handler 的工作流程

3.1 子线程发送消息(Message)对象 msg

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.2 将消息对象添加到 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 (;;) {
                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.2 主线程取消息,处理消息

主线程 main 函数,ActivityThread main() ,主要做两件事情:

  1. Looper.prepareMainLooper();
  2. Looper.loop();
public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    Looper.loop();
}

来看一下 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.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }

    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 =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    me.mSlowDeliveryDetected = false;

    for (;;) {
        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,否则就进入一个阻塞状态,一直等到有新的消息入队。

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