Handler 机制

Handler

因为 Android 只允许在主线程更新 UI ,所以我们在子线程获取到的数据需要切换线程到主线程再做 UI 处理,而 Handler 是一种用于线程间消息传递的机制,能够实现线程切换的功能。

基本使用方式

步骤一: 创建 Handler 实例

 // callback 方式
private Handler callbackHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // 更新UI等操作
        return true;
    }
});

// 成员内部类
private Handler innerHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // 更新UI等操作
    }
};

// 静态自定义 Handler 内部类
private static class MyHandler extends Handler {

    private WeakReference<MainActivity> mReference;

    public MyHandler(MainActivity activity) {
        mReference = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        // 更新UI等操作
        if (mReference.get() != null) {
            switch (msg.what) {
                case 0:
                    Toast.makeText(mReference.get(), msg.obj.toString(), Toast.LENGTH_SHORT).show();
                    break;
                case 1:
                    Toast.makeText(mReference.get(), "这是空消息方式", Toast.LENGTH_SHORT).show();
                    break;
                case 2:
                    Toast.makeText(mReference.get(), "这是延时空消息方式", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
}

总结:通常使用自定义静态内部类的方式来使用,能够避免内存泄漏。

步骤二: 调用 Handler 相关send 或 post 方法

 // 子线程中发送消息
new Thread(new Runnable() {
    @Override
    public void run() {
        Message message = Message.obtain();
        message.what = 0;
        message.arg1 = 100;
        message.obj = "普通消息方式";

        // send 方式发送普通消息和延时消息
        mMyHandler.sendMessage(message);
        mMyHandler.sendMessageDelayed(message, 2000);

        // 发送空消息
        mMyHandler.sendEmptyMessage(1);
        mMyHandler.sendEmptyMessageDelayed(2, 5000);

        // post 方式发送普通消息
        mMyHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "Runnable 消息方式", Toast.LENGTH_SHORT).show();
            }
        });

        // post 方式发送延时消息
        mMyHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "Runnable 延时消息方式", Toast.LENGTH_SHORT).show();
            }
        }, 1000);
    }
}).start();

步骤三: 移除相关消息

这一步不是必须的,是为了确保避免内存泄露风险。

@Override
protected void onDestroy() {
    super.onDestroy();
    mMyHandler.removeCallbacksAndMessages(null);
}

总结: 通常发送消息的方式有 3 种,分别是 sendMessage 、 sendEmptyMessage 、post 方法,每种方式又有延时发送的方法,分别是 sendMessageDelayed 、 sendEmptyMessageDelayed 、 postDelayed,延时发送的时间单位为毫秒,即多少毫秒后再发送。

源码分析

以下相关源码会省略一些跟分析无关的代码,只保留关键代码。

ActivityThread

在应用线程的入口类 ActivityThread 的 main 方法中,有如下调用:

public static void main(String[] args) {

    // 创建主线程 Looper
    Looper.prepareMainLooper();
    // Looper 开始工作
    Looper.loop();

}

在 mian 方法中,调用了跟 Looper 相关的两个方法,接下来我们看一下 Looper 类。

Looper

先看一下 Looper 类重要的成员变量

public final class Looper {

    // ThreadLocal 绑定线程和 Looper 对象
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    // 主线程的 Looper 对象
    private static Looper sMainLooper;

    // 消息队列
    final MessageQueue mQueue;

    // 当前线程
    final Thread mThread;

}

我们先看 prepareMainLooper 相关方法:

public static void prepareMainLooper() {
    // false 表示不允许退出
    prepare(false);
    synchronized (Looper.class) {
        // 这里判断 Looper 是否已经存在了,只允许有一个
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

// 该方法主要给子线程调用
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    // 这里判断 Looper 只允许有一个,方法只能调用一次
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 初始化 Looper 对象并放进 ThreadLocal 里面
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    // 创建消息队列
    mQueue = new MessageQueue(quitAllowed);
    // 初始化当前线程 ,线程关联从这里开始
    mThread = Thread.currentThread();
}

prepareMainLooper() 方法内容归纳:

  1. 判断 Looper 是否已经存在,存在则报异常。

  2. 初始化 Looper 对象并放进 ThreadLocal 里面。

  3. Looper 里面初始化了 MessageQueue 对象并初始化了当前线程。

现在,我们看一下 loop() 方法,:

public static void loop() {
    // 从 ThreadLocal 中获取当前线程的 Loop
    final Looper me = myLooper();
    // 如果 Looper 还没初始化,则报错
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 获取 Looper 里面的消息队列
    final MessageQueue queue = me.mQueue;
    for (;;) {// 死循环
        // 取出消息,可能会阻塞线程
        Message msg = queue.next(); // might block
        if (msg == null) {
            // 消息为空 ,结束循环
            return;
        }
        try {
            // 回调 Handler 的 dispatchMessage 方法
            msg.target.dispatchMessage(msg);
        } finally {
        }
        // 对分发了的消息进行回收操作
        msg.recycleUnchecked();
    }
}

loop() 方法里面的逻辑归纳为:

  1. 先判断 Looper 对象是否已经初始化。

  2. 在死循环里面不断调用 Looper 的成员 MessageQueue 的next() 方法来获取 Message 对象。

  3. 取出的消息不为空则回调 Message 对象的成员 target 的 dispatchMessage 方法 ( tartget 其实就是 Handler )。

  4. 对分发了的消息进行回收操作。

Handler

Looper 中会回调 Handler 的 dispatchMessage 方法,那么我们先看一下 Handler 类重要的成员变量和方法:

public class Handler {

    // 当前线程对应的 Looper 
    final Looper mLooper;

    // 指向 Looper 的 MessageQueue
    final MessageQueue mQueue;

    // Callback 对象
    final Callback mCallback;

    // 是否是异步消息,默认同步
    final boolean mAsynchronous;

    // Callback 接口对象
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    // 该方法需要子类覆写实现逻辑操作
    public void handleMessage(Message msg) {
    }

}

构造方法

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) { //该构造给主线程调用
    // 指向 Looper 对象    
    mLooper = Looper.myLooper();
    // 必须要有Looper,不然报异常
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // 指向 Looper 的 mQueue
    mQueue = mLooper.mQueue;
    // 赋值回调接口对象
    mCallback = callback;
    // 赋值是否异步
    mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
    // 此构造给那些在子线程创建 Handler 时使用,需要主动调用 Looper.prepare() 方法
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler 类主要面向开发者,所以我们需要全面掌握里面的方法。首先是构造函数,共 6 个构造函数,但是最终会往两个构造函数调用,这两个构造函数分别给主线程和子线程调用,具体看代码注释。

obtainMessage 方法

public final Message obtainMessage()
{
    return Message.obtain(this);
}

public final Message obtainMessage(int what)
{
    return Message.obtain(this, what);
}

public final Message obtainMessage(int what, Object obj)
{
    return Message.obtain(this, what, obj);
}

public final Message obtainMessage(int what, int arg1, int arg2)
{
    return Message.obtain(this, what, arg1, arg2);
} 

public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
    return Message.obtain(this, what, arg1, arg2, obj);
}

Handler 里面有多个 obtainMessage 重载方法,用于获取 Message 对象,其实内部是调用 Message 的 obtain 静态方法的。所以我们也可以通过 Message 的静态 obtanin 方法来获取 Message 对象,最好不要直接用 new 关键字来创建 Message 对象,原因后面会说。

发送 Message 方法

public final boolean post(Runnable r)
{
    // 延迟0秒发送
   return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postAtTime(Runnable r, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean sendMessage(Message msg)
{
    // 一般使用这个方法,延时值为0
    return sendMessageDelayed(msg, 0);
}

public final boolean sendEmptyMessage(int what)
{    
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    // 延时时间再执行,时间为毫秒单位,延时值加上系统的当前时间作为发送时间
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

private static Message getPostMessage(Runnable r) {
    // 创建 Message 对象并把Runnable对象赋值给 Message 的 callback
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

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

Handler 里面也有很多发送消息的方法,有 3 种发送方式:

  1. post() 类型的方法,传入的是 Runnable 对象,内部会通过 getPostMessage() 方法来封装 Message 对象,并把 Runnable 对象赋值给 Message 的 callback 成员对象。

  2. sendEmpty() 类型的也不需要传入 Message 对象,内部会通过 Message.obtain() 方法获取 Message 对象。

  3. send() 类型则需要外部传入 Message 对象。

无论调用何种方式发送消息,最终会调用 enqueueMessage 方法将消息对象入队,代码如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 把当前 Handler 传给 target 变量,用于分发回调
    msg.target = this;
    if (mAsynchronous) {
        // 设置为异步消息
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

该方法会调用 MessageQueue 的 enqueueMessage 方法来将消息入队,注意方法内调用了 msg.target = this ,结合上面 Looper 的 loop 方法,会回调 Handler 的 dispatchMessage() 方法, dispatchMessage() 的源码如下:

// 消息分发
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        // 如果接收的消息中,携带这一个 callback ,就执行
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // 如果实例化 Handler 时实现了接口回调,就执行
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 执行这个方法
        handleMessage(msg);
    }
}

// 运行消息的 Callback 接口对象
private static void handleCallback(Message message) {
    message.callback.run();
}

方法归纳:

  1. 首先判断 Message 的 callback 对象是否为空,由之前分析得知,当用 post 方式发送消息时,callback 则不为空,此时会运行 callback 的 run 方法,即 post 传入的 Runnable 的 run 方法。

  2. 然后会判断 mCallback 是否为空,如果不为空,则回调 mCallback 的 handleMessage 方法,此时的 mCallback 由创建 Handler 的时候通过构造方法传入,如果 handleMessage 方法返回 true ,则不执行最后的 Handler 的 handleMessage 方法,如果返回 false ,则执行。

  3. 执行 Handler 的 handleMessage 方法,该方法通常会被子类覆写。

总结(消息分发执行的优先级): post 的方式 > callback 方法 > 覆写方式 。

MessageQueue

上述讲到入队的时候,会调用 MessageQueue 的 enqueueMessage 方法,我们先看一下 MessageQueue 的构造方法,只有一个构造方法,如下:

MessageQueue(boolean quitAllowed) {
    // 用来标识是否允许退出,主线程是不允许退出的,不然就会退出整个程序
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

入队方法源码如下:

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) {
        // 已经退出,直接回收 Message ,返回 false
        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) {
            // New head, wake up the event queue if blocked.
            // 把 msg 设置为新的链表头
            msg.next = p;
            // 将 mMessages 指向当前的链表头
            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;
        }

        // 唤醒线程,native 方法,添加Message 到 C环境中的消息队列中
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

方法归纳:

  1. 判断 Message 的 target 对象,即 Handler 是否存在,不存在报异常。

  2. 判断当前 Message 是否已经在使用,是则报异常,避免重复发送同一个消息。

  3. 判断是否已经退出循环了,如果是,则直接回收 Message ,返回 false ,入队失败。

  4. 标记当前 Message 已经在使用,然后把当前 Message 插入到链表对应的位置上,按时间执行从早到晚排序。

在上述 Looper 的 loop 方法中,会不断循环地调用 MessageQueue 的 next 方法来做出队处理,获取 Message 来分发回调,next 方法如下:

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; 
    //  0 表示不会阻塞,立即返回
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        // 根据 nextPollTimeoutMillis 判断是否让其处于线程阻塞状态
        // 取出一个消息 ,native方法,在C环境中创建了一个 NativeMessageQueue 数据对象
        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) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    // 还没到执行时间,设置最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 时间到了,下面的逻辑是把消息从链表中取出来
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    // 把当前消息指向的下一个消息的引用置为 null,
                    msg.next = null;

                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    // 标记已经使用
                    msg.markInUse();
                    return msg;
                }
            } else {
                没有消息的时候,一直阻塞不会超时
                nextPollTimeoutMillis = -1;
            }

            // 如果调用了 quit 方法了,则返回 null ,Looper 就会结束循环
            if (mQuitting) {
                dispose();
                return null;
            }

            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // 没有 idle handlers 要处理,Looper 继续阻塞等待
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 处理一些不紧急的任务
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        //在处理 idle handler 的时候可能会有新的消息过来, 所以需要设置为 0 立即去取一次消息,
        //当没有 idle handler 的时候会调 continue 阻塞。
        nextPollTimeoutMillis = 0;
    }
}

方法归纳:

  1. 如果没有消息或没到消息执行时间,会阻塞线程,否则唤醒线程。

  2. 获取最早的异步消息并返回。

  3. 如果调用过 quit 退出方法,则返回空,Looper 就会结束循环。

  4. 处理一些不紧急的 idle handler 任务。

注意:主线程调用 quit 方法会报异常,默认不能退出,quit 方法代码如下:

void quit(boolean safe) {
    // 主线程不允许退出循环
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        // 设置已经在退出
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

从源码可以看出当我们调用 quit 方法时,如果 safe 为 false 时,执行的是 removeAllMessagesLocked 方法,该方法主要是把 MessageQueue 消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过 sendMessageDelayed 或通过 postDelayed 等方法发送)还是非延迟消息。

当 safe 为 true 时,其内部调用的是 removeAllFutureMessagesLocked 方法,该方法只会清空 MessageQueue 消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让 Handler 去处理完成后才停止 Looper 循环。

Message

在整个 Handler 机制中,Message 就像信件一样在其中传递,作为线程消息传递的物件,我们看看 Message 类。

public final class Message implements Parcelable {

    // 让接收者知道消息是做什么的
    public int what;

    // 用来传递整型参数
    public int arg1;

    // 用来传递整型参数
    public int arg2;

    // 用来传递复杂对象
    public Object obj;

    // 标记消息被使用
    /*package*/ static final int FLAG_IN_USE = 1 << 0;

    // 标记消息是异步的
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    // 消息当前的标记
    /*package*/ int flags;

    // 执行的时间
    /*package*/ long when;

    // 创建Handler时,target会被赋值给对应的 Handler
    /*package*/ Handler target;

    // Handler 用 post 消息发送消息时,会被赋值
    /*package*/ Runnable callback;

    // Bundle 对象
    /*package*/ Bundle data;

    // 指向的下一个 Message ,是个链表结构
    /*package*/ Message next;

    // 消息池链表头对象
    private static Message sPool;

    // 当前消息池的消息数量
    private static int sPoolSize = 0;

    // 消息池最大维护量
    private static final int MAX_POOL_SIZE = 50; 

    public static Message obtain() {
        // 尽量用这个方法来创建消息,消息池不为null ,则直接取出来复用消息
        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() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                // 维护消息池
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

    /*package*/ boolean isInUse() {
        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
    }

    /*package*/ void markInUse() {
        flags |= FLAG_IN_USE;
    }

    // 默认 public 构造方法,但是最好不要用
    public Message() {
    }

}

Handler消息机制的工作原理总结

通过Handler发送消息,此时会将Message入队列到MessageQueue中,并且唤醒等待的Looper
Looper获取的消息会投递给对应的Handler处理

  1. 创建与线程绑定的 Looper ,同时会创建一个与之关联的 MessageQueue 用于存放消息。

  2. 开启消息循环,从 MessageQueue 中获取待处理消息,若无消息会阻塞线程。

  3. Handler 会调用 MessageQueue 的 enqueueMessage 方法,将 Message 入队并且唤醒等待的 Looper 。

  4. Looper 会循环调用 MessageQueue 的 next 方法,将 Message 出队并回调 Message 的 target 对象的 dispatchMessage 方法对 Message 进行分发处理, target 就是对应的 Handler 对象。

时序图如下:

答疑解惑

其实以下这些疑问在上述分析中都能找到答案,这里再简要说明一下原因。

为什么 Handler 能够切换线程执行?

我们在子线程发送的消息,最终会在 Looper 所在的线程进行分发并回调,而 Looper 默认就是运行在主线程的,也就是说回调的 handleMessage() 方法是运行在主线程的,也就达到了线程切换的目的。因此,handleMessage() 方法执行的线程就是 Looper 所在的线程,而与 Handler 创建时所在的线程无关。

示例调用链:

为什么我们能在主线程直接使用 Handler,而不需要创建 Looper?

因为在程序启动的时候,系统会自动初始化 Looper 并调用 loop 方法,上述源码有讲到。

为什么创建 Message 对象推荐使用 Message.obtain()获取?

Message 类内维护了一个 Message 缓存池,用 obtain 方法时,会优先在池内取 Message 对象,达到节省开销、减少内存消耗的目的。缓存池的来源就在 Message 的 recycleUnchecked 方法中,也就是在 Message对象被回收资源时,会添加到缓存池内,缓存池最大量为 50 。

子线程可以创建 Handler 吗?

可以,创建 Handler 跟线程没关系,只需按照 Handler 的构造创建即可。因为系统只在主线程自动初始化 Looper 并调用 loop 方法,所以在子线程需要主动调用 Looper.prepare() 方法创建 Looper ,然后调用 loop() 方法开始循环工作。

子线程里使用示例代码:

// 子线程中发送消息
new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        threadHandler = new Handler(Looper.myLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                Log.d("MainActivity", "当前线程名称 == " + Thread.currentThread().getName());
                return true;
            }
        });
        Looper.loop();
        Message message = Message.obtain();
        message.what = 0;
        message.obj = "普通消息方式";
        mMyHandler.sendMessage(message);
    }
}).start();

mMyHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        threadHandler.sendEmptyMessage(1);
    }
}, 2000);

Handler(Callback) 跟 Handler() 这两个构造方法的区别在哪?

上述分析有讲到,只是执行的优先级不同,callback 方法优先执行,而且如果返回 true ,还可以拦截 handleMessage 的回调。

Handler 引起的内存泄露原因以及最佳解决方案是什么?

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

解决该问题的最有效的方法是:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息,具体代码在上述 Handler 使用示例中有。