Handler消息机制

说起Android的消息处理机制, Handler肯定是无法被忽略的一个点. 通过Handler收发消息可以非常方便的实现线程间通信功能.

Handler的基本使用方式

实际开发中, Handler的使用方式通常是这样的:

Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        // 处理消息
    }
};
... 
// 各种姿势发送消息
handler.obtainMessage();
handler.sendMessage();
handler.sendEmptyMessage();    

或者是这样的:

handler.post(() -> {
    // 执行任务
});
// 延迟 1 s 执行任务
handler.postDelayed(() -> {}, 1000);

实际上还有我们爱用的 runOnUiThread(), 里面也使用了 Handler.

// Activity 公共方法
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

Handler可以说是无处不在. 下面通过简单的源码研究, 了解一下 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(Callback callback, boolean async) {
  ... 省略 ...
  mLooper = Looper.myLooper();
  // Looper 为空, 就抛出异常
  if (mLooper == null) {
      throw new RuntimeException(
          "Can't create handler inside thread " + Thread.currentThread()
                  + " that has not called Looper.prepare()");
  }
  // 消息队列 - MessageQueue, 由 Looper维护.
  mQueue = mLooper.mQueue;
  mCallback = callback;
  mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

可以发现两者的区别就是和 looper 有关的那一部分. 我们大概也知道, Looper 的作用, 就是帮助 Handler不断从消息队列中取出要处理的消息. 如果在子线程创建 Handler时, 一定要先调用 Looper.prepare(), 否则将抛出上面那段异常.

new Thread(() -> {
    Looper.prepare();
    Handler handler = new Handler(){
        ...
    };
    // 开始循环处理消息
    Looper.loop();
}).start();

而在主线程, 则不需要 Looper.prepare() 就可以直接创建 Handler. 因此应该是在某个地方已经帮我们创建好了 Looper. 我们知道Android程序的UI线程(主线程) 是个永不退出的消息循环, 程序的入口则在 ActivityThread 这个类里.

public final class ActivityThread extends ClientTransactionHandler {
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
    }
}

继续看 prepareMainLooper():

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

再看看 prepare() 吧:

public static void prepare() {
    prepare(true);
}
// quitAllowed 表示looper是否允许退出
private static void prepare(boolean quitAllowed) {
    // 一个 ThreadLocal, 只有一个 Looper
    // 解释下ThreadLocal: ThreadLocal是线程本地存储区, 每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能相互访问.
    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) {
    // 创建消息队列 MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

到这里, 我们已经知道, Handler需要依靠 Looper 来循环获取消息. 而在子线程创建Handler时, 需要我们自己创建Looper. Looper 保存在 ThreadLocal中, 且一个线程只会有一个 Looper, 而Looper中则维护着一个MessageQueue(消息队列). 因此在同一个线程中, 不管有多少个 Handler, 它们都共用一个 Looper和一个消息队列.

Handler的构造方法先看到这里, 下面在看看 Handler 发送消息相关的方法:

发送消息之 sendMessage

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
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);
}

还有其它的 sendXXX方法, 就不一一列出了, 反正最终都调用了 enqueueMessage()方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 注意这里, 将 msg.target 设置为当前 Handler
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 调用了 MessageQueue 的 enqueueMessage() 方法将消息放入消息队列
    return queue.enqueueMessage(msg, uptimeMillis);
}

继续看看 MessageQueue.enqueueMessage() 是怎么添加消息的

boolean enqueueMessage(Message msg, long when) {
    // 上面说了, msg.target 指向的是 Handler
    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) {
            // 将新添加消息的 next 指向 p   --- 可以发现, "消息队列" 其实是个单向链表结构
            msg.next = p;
            // 将新添加消息设置为当前要处理的消息
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 将消息按时间顺序插入到MessageQueue
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                // p 设置为上一条消息
                prev = p;
                // 将 p 指向其原来的下一条消息
                p = p.next;
                // 如果到了结尾了, 则跳出循环
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 将 新消息添加到链表中
            msg.next = p; 
            prev.next = msg;
        }

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

看完 sendMessage(), 下面再看看发送消息的另外一种方式, post()

发送消息之 post

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

可以发现, 其实还是调用了 sendMessage 方法. 不过有一点不同是多了一个 getPostMessage, 用来从一个 Runnable 生成一个 Message.

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    // 注意这里将 runnable设置给了 message.callback
    m.callback = r;
    return m;
}

到这里, 关于将消息添加到消息队列的部分算是结束了. 最后在看下 Looper是如何循环获取消息的.

处理消息

public static void loop() {
    // 从 ThreadLoacal中取出 Looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 拿到 Looper 中的消息队列 MessageQueue
    final MessageQueue queue = me.mQueue;
    ... 省略 ...
    // 循环
    for (;;) {
        // 通过 messageQueue.next() 取出下一条要处理的消息
        Message msg = queue.next(); 
        if (msg == null) {
            // 没有要处理的消息, 结束循环
            return;
        }
        ... 又省略了 ...
        try {
            // 将消息交给 Handler 处理.  还记得 msg.target 就是 Handler 吗
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            ...
        }
        ...
        // 消息回收. 为了提高效率, 避免频繁的创建 Message. 因此其维护了一个'消息池', 便于复用
        msg.recycleUnchecked();
    }
}

快要结束了, 再瞄一眼 MessageQueue 的 next() 方法.

Message next() {
    ... 省略 ...
    for (;;) {
        ...
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            // 记录当前要处理的 Message
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Handler 为空, 则向后查找
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 该消息还没到处理的时候
                    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;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 为空, 没有要处理的消息
                nextPollTimeoutMillis = -1;
            }
            ...
        }
        ...
    }
}

最后再看下 Handler 的 dispatchMessage() 方法:

public void dispatchMessage(Message msg) {
    // 还记得 post 发送消息吗? msg.callback 就是 post方法中传过来的 Runnable
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 如果创建 Handler 时, 传入了 Callback, 则交给 Callback 去处理 (一般好像都没传过)
        // public Handler(Callback callback)
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 终于找到了 handleMessage
        handleMessage(msg);
    }
}
...
private static void handleCallback(Message message) {
    // 调用 Runnable 的 run 方法
    message.callback.run();
}

总结

  • 整个消息机制中, 涉及到了 Handler、Message、Looper、ThreadLocal、MessageQueue等对象
  • Message就是需要传递的消息, 里面可以携带数据;
  • Handler是一个面向开发者的辅助类, 平时我们就用它发送和处理消息;
  • MessageQueue里面维护了一个消息队列(其实是一个单链表结构). 我们通过Handler发送消息时, 其实最终也是调用了 MessageQueue的方法将消息添加到队列中.
  • Looper则有两个作用, 一是将它内部的 MessageQueue 提供给Handler, 而是不断循环从MessageQueue中去除消息交给Handler处理.
  • 另外, 每个线程只有一个Looper, 其保存在ThreadLoacal中. 同一个线程中的所有Handler都共用此一个Looper, 以及它里面的一个MessageQueue.
  • 最后, Handler的工作离不开Looper, 因此创建Handler前要通过 Looper.prepare() 创建 Looper, 同时要通过 Looper.loop()开启循环. 而主线程中之所以不用我们手动创建, 是因为ActivityThread已经帮我们创建好了.

拓展-Handler内存泄漏

通过前面的分析, 我们知道 message.target持有 Handler的引用, 如果又用常规的内部类的方式使用 Handler, 则 Handler又持有其外部类的引用(比如Activity), 因此可能导致内存泄漏. 因此可以Handler定义为单路的类, 或者使用静态内部类, 并通过弱引用的方式持有 Activity.

private static class MyHandler extends Handler {
    private WeakReference<MyActivity> mActivity;
    public MyHandler(MyActivity activity) {
        this.mActivity = new WeakReference(activity);
    }
    @Override
    public void handleMessage(final Message msg) {
        MyActivity activity = mActivity.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}
// ... 别忘了再页面销毁时清除消息
protected void onDestroy() {
    mHandler.removeCallbacksAndMessages(null);
    super.onDestroy();
}

  转载请注明: 四月一号 Handler消息机制

 上一篇
TheadLocal源码分析 TheadLocal源码分析
默认情况下, 多线程间数据是共享的, Java中也提供了相关的同步机制用于多线程数据共享造成的数据安全性问题. 同时考虑到某些需要线程间数据隔离的场景, 也提供了ThreadLocal让我们可以管理属于线程私有的数据. ThreadLoca
2019-03-24
下一篇 
自定义View(六)--Paint 自定义View(六)--Paint
构造方法 直接构造方法// Create a new paint with default settings public Paint() // 使用指定的 flags 初始化 paint. 后续也可以通过 setFlags() 去改变这些
2019-03-21
  目录