Handler机制源码详解

164 阅读11分钟

Handler 是一个消息分发对象。handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发消息,也可以通过它处理消息。

以下是一段常见的Handler在进行多线程通信时的代码(使用Kotlin语言实现),在主线程中开启子线程,在子线程中通过主线程的Handler向主线程发送Message,最后在主线程中接收消息并处理:

/**
 * 运行后打印日志:
 * -------->>Starting sub thread...
 * -------->>Sending message at thread `Thread-2`...
 * -------->>Message received at thread `main`...
 */
class MainActivity : AppCompatActivity() {

    private val customHandler: Handler = CustomHandler()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        println("-------->>Starting sub thread...")
        CustomThread().start()
    }

    inner class CustomThread : Thread() {
        override fun run() {
            println("-------->>Sending message at thread `${Thread.currentThread().name}`...")
            val msg = Message.obtain()
            customHandler.sendMessage(msg)
            super.run()
        }
    }

    class CustomHandler : Handler() {
        override fun handleMessage(msg: Message?) {
            super.handleMessage(msg)
            println("-------->>Message received at thread `${Thread.currentThread().name}`...")
        }
    }
}

下面对上述代码进行源码分析,首先看Handler的创建过程。以下列出Handler的所有构造函数及其相互之间的调用关系:

public Handler() { this(null, false); }

public Handler(Callback callback) { this(callback, false); }

public Handler(Looper looper) { this(looper, null, false); }

public Handler(Looper looper, Callback callback) { this(looper, callback, false); }

public Handler(boolean async) { this(null, async); }

public Handler(Callback callback, boolean async) {
    // ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

可以发现以下几点:第一,所有其他构造方法最终都会调用到最后两个构造方法;第二,在构造方法中做的事情都是校验参数和为私有变量赋值;第三,async这个参数在不手动传值时,永远被置为false

再来看其余两个参数CallbackLooper

Callback是一个接口,里面唯一的抽象方法就是我们熟悉的handleMessage()方法,用于在接收到消息时回调,代码如下:

public interface Callback {
    public boolean handleMessage(Message msg);
}

想了解Looper是什么,先看一下源码中的注释:

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call {@link #prepare} in the thread that is to run the loop, and then {@link #loop} to have it process messages until the loop is stopped.

大致意思是,Looper是用来在线程中运行的消息循环,线程中默认是没有Looper的,需要通过依次调用Looper.prepare()Looper.loop()来创建。那么问题来了,这两个方法里面都做了什么呢?为什么这两个方法中都没有传入线程对象,就可以绑定到线程呢?Looper的作用到底是什么呢?带着这些问题,我们来看一下Loope类的源码,先来看与Looper#prepare()方法相关的源码:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;

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

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));
}

从上述代码中可以总结出prepare()方法的执行流程:创建一个新的Looper对象,初始化其两个成员变量mQueuemThread,然后将这个Looper存储到一个ThreadLocal对象中。从以上源码可以总结出以下几点:第一,ThreadLocal是线程私有的,因此这个Looper只被这一个线程享有;第二,一个线程中最多只能有一个Looper,因为如果调用多次prepare()方法,会抛出异常Only one Looper may be created per thread;第三,在哪个线程中调用Looper.prepare(),创建出来的Looper就会绑定到哪个线程,因为mThread变量的初始化方式是Thread.currentThread(),即当前所在线程。

综合以上信息,可以得出结论:一个线程可能绑定了0个或1个Looper,但一个Looper一定绑定了一个线程

再来看Looper#loop()方法的源码,由于此方法中代码较多,因此这里只贴出核心代码:

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
        }
        msg.recycleUnchecked();
    }
}

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

可以看到,在loop()方法中首先通过myLooper()方法,从ThreadLocal中获取当前线程的Looper对象,然后获取该Looper中的MessageQueue消息队列;随后开启了一个无限循环,从消息队列中无限循环的取出Message对象,并调用其target对象的dispatchMessage()方法。这里最终操作的对象是Message,因此我们先来看一下Message类的源码:

public final class Message implements Parcelable {
    static final int FLAG_IN_USE = 1 << 0;
    Handler target;
    Message next;
    Runnable callback;
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        // ... reset data
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
}

Message的核心源码中可以总结出以下几点:第一,Message是一个链表结构,因为其next属性指向了一个Message类型的对象;第二,Message类中维护了一个静态的sPool对象,即一个对象池,也是一个链表结构,用于方便的存取Message对象,提升性能;第三,官方推荐使用的Message.obtain()方法,其实就是从sPool对象池中取出头节点使用;当调用recycleUnchecked()方法回收对象时,会将重置后的Message对象添加到sPool对象池的头部;第四,Message类中的target属性是一个Handler类型的对象,即要处理此消息的对象。

再来看MessageQueue#next()方法相关的源码:

Message next() {
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null) {
                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 {
                nextPollTimeoutMillis = -1; // No more messages.
            }
            // ...
            return null;
        }
    }
}

从源码中可以看出,MessageQueue#next()方法的原理是:取出队列中的第一条消息,并判断这条消息的预计执行时间是否晚于当前时间,如果晚于当前时间则说明现在还不是执行这条消息的时机,此时会调用Binder.flushPendingCommands()nativePollOnce(ptr, nextPollTimeoutMillis)(这两个方法都是被native关键字声明的方法,底层调用的是操作系统的原生方法),将这段时间差值过渡过去,直到队列中的第一条消息达到了执行时机,此时才返回这条消息。这就是为什么在调用Handler#postDelayed()方法时,发出的消息会在延迟一段时间后才执行。

到此我们不免会有一个新的疑问:假如我们通过Handler依次发出三条消息,其延时分别是2000ms10ms3000ms,而MessageQueue每次调用next()方法都是返回第一条消息,其内部是怎样处理这些消息的存放顺序的呢?接下来我们来看一下MessageQueue#enqueueMessage()方法的源码:

Message mMessages;

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) {
        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;
        } else {
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }
    }
    return true;
}

可以看到,MessageQueue中存储的Message是根据消息执行的时间先后来排序的,在队列为空、消息没有延时、消息执行时间早于当前时间这三种情况下,消息会被直接添加到队列的头部;否则,则从头开始遍历队列中的消息,将消息插入到队列中,以保证当前消息前面的消息都早于它被执行,当前消息后面的消息都晚于它被执行。

到此为止,LooperMessageQueueMessage相关的代码就都分析完了,接下来看一下Handler在发送消息时都发生了什么。Handler中用来发送消息的方法有很多,下面列出部分常见方法的签名:

public final boolean post(Runnable r) {}

public final boolean postAtTime(Runnable r, long uptimeMillis) {}

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {}

public final boolean postDelayed(Runnable r, long delayMillis) {}

public final boolean postDelayed(Runnable r, Object token, long delayMillis) {}

public final boolean sendMessage(Message msg) {}

public final boolean sendEmptyMessage(int what) {}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {}

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

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {}

上述方法中,如果参数中传了Runnable,则会将这个Runnable赋值给Message#callback;如果是sendEmptyMessageXXX(),则会先通过Message.obtain()方法从消息池中取出一个消息,然后设置其what属性。但最终,上述方法都会调用下面这个方法:

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

可以看到,enqueueMessage()方法主要做了以下两件事:

  • Message对象的target属性设置为自己,这样就形成了一个闭环:本Handler在子线程中发送消息,由于消息关联的target属性也是本Handler,因此本Handler也是消息最终的处理者。消息虽然存储在子线程的Looper关联的MessageQueue中,但它持有本Handler的引用,因此当消息被执行时,实际上也是已经回到了当前的线程中。
  • Message入队列到本Handler关联的MessageQueue中。需要强调的是,这里的uptimeMillis是通过SystemClock.uptimeMillis() + delayMillis方法计算出来的,即用当前系统启动时间加上延时时间,得到的是一个具体的时间戳。

还记得Looper#loop()方法中,获取到消息后执行的msg.target.dispatchMessage(msg)吗,现在已经知道了msg.target实际上就是发出这个消息的Handler,那么我们再来看看Handler#dispatchMessage()方法中做了什么吧:

final Callback mCallback;

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

private static void handleCallback(Message message) {
    message.callback.run();
}

public void handleMessage(Message msg) {
}

可以看到,这里进行了多重条件判断:

  • 如果Message中设置了callbackRunnable),则调用其run()方法,结束;
  • 如果Message中没有设置callback,则判断本Handler中有没有设置mCallbackCallback),如果有则调用其handleMessage()方法,判断其返回值,如果返回true则结束;
  • 如果上述几个条件都不符合,则会调用本Handler中的handleMessage()方法,这个方法可以被重写。

综上可得,Handler中事件处理的优先级是:Message#Runnable#run() > Handler#Callback#handleMessage() > Handler#handleMessage()

最后总结一下Handler机制

  • 创建Handler的时候,通过Looper.myLooper()获取当前线程下的Looper对象(如果当前线程下没有Looper会报错,因此子线程中需要先通过Looper.prepare()Looper.loop()创建并启动一个Looper对象),然后将Looper对象和Looper#MessageQueue对象赋值给当前Handler
  • Handler对象通过postXXX()sendXXX()方法发送消息,最终都会调用到enqueueMessage()方法,这个方法做了两件事:将当前Handler赋值给Message#target对象、将Message对象入队列到MessageQueue中;
  • MessageQueue中按照Message被执行的时间早晚进行排序,新入队列的Message会被插入到队列中合适的位置,以保证时间的先后顺序;
  • 当试图从MessageQueue中取出一条消息时,会判断队列中的第一条消息是否达到了执行时间,如果没有达到,则会调用操作系统层的native方法进行时间过渡,直到达到消息的执行时间为止;
  • 当调用了线程中Looper对象的loop()方法时,会无限循环的从MessageQueue中取出消息,取出消息后即会调用msg.target.dispatchMessage()方法,即将消息派发给发送这条消息的Handler,由这个Handler处理消息(此时已经回到了Handler被创建时的线程);
  • Handler会按照Message#Runnable#run() -> Handler#Callback#handleMessage() -> Handler#handleMessage()的优先级顺序执行Message

补充一些上面没有提到的细节

1. 怎样在子线程中使用Handler

在子线程中使用Handler的方式有两种。

第一种方式是上面说到的,由于Handler依赖Looper实现,而子线程中默认又没有Looper,因此我们需要先创建Looper,然后再创建并使用Handler。具体方法如下(以下方法使用Kotlin语言实现):

inner class CustomThread : Thread() {
    override fun run() {
        super.run()
        // 创建 Looper 并与当前线程(子线程)绑定
        Looper.prepare()
        // 创建 Handler 对象,此时因为已经初始化了 Looper,因此不会报错
        val handler = object : Handler() {
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
            }
        }
        // ... 操作 Handler 对象
        // 别忘了调用 Looper.loop() 方法,启动 Looper
        Looper.loop()
    }
}

第二种方法是使用HandlerThread代替Thread创建子线程。HandlerThreadThread的子类,其中默认实现并持有了一个Handler,源码如下:

public class HandlerThread extends Thread {
    Looper mLooper;
    private @Nullable Handler mHandler;

    // ...

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    public Looper getLooper() {
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
}

2. 主线程中不需要初始化Looper,为什么可以使用Handler

答案是:Android系统已经在主线程中帮我们初始化好了Looper。看以下一段ActivityThread类中的代码:

public final class ActivityThread extends ClientTransactionHandler {

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

可见,在应用刚运行起来的时候,主线程中的Looper就已经被创建了。接下来看一下Looper.prepareMainLooper()中的代码:

private static Looper sMainLooper;  // guarded by Looper.class

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

可以看到,Looper.prepareMainLooper()方法最终就是调用了Looper.prepare()方法。

3. Handler导致的内存泄漏问题

参考:关于HandlerLeak的一点理解