Android|Android 消息机制 Handler 详解

Handler
Android|Android 消息机制 Handler 详解
文章图片
一些Handler子类
Handler 允许你发送和处理,与线程的 MessageQueue 关联的 Message 和 Runnable 对象。每个 Handler 实例都与单个线程和该线程的消息队列相关联。当你创建一个新的 Handler 的时候,它绑定到正在创建它的线程的消息队列。从这时开始,Handler 可以把消息或可执行的任务传递给消息队列,并当消息被读取的时候执行它们。

  • There are two main uses for a Handler
    Handler的两个主要用处:
    • to schedule messages and runnables to be executed as some point in the future;
      在将来的某个时候安排消息和子线程任务的执行。
    • to enqueue an action to be performed on a different thread than your own.
      添加在不同线程执行的事件。
Scheduling messages is accomplished with the
  • post(Runnable),
  • postAtTime(Runnable, long),
  • postDelayed(Runnable, long),
  • sendEmptyMessage(int),
  • sendMessage(Message),
  • sendMessageAtTime(Message, long),
  • sendMessageDelayed(Message, long) methods.
post 允许添加 Runnable 对象入队列,以便在收到消息队列时被消息队列调用;sendMessage 允许添加一个Message对象入队列,Message 对象包含一系列数据,它将由 Handler 的 handleMessage(Message) 方法处理。
当 post 或 sendMessage 到 handler 时,可以允许在消息队列准备就绪后立即处理该项目,或者在处理消息之前指定延迟或规定执行的时间。后两者允许你实现 timeout,ticks 和其他 timing-based (基于时间)的行为。
为应用程序创建一个进程时,其主线程将专门运行一个消息队列,负责管理顶层应用程序对象(活动,广播接收器等)以及它们创建的任何窗口。通过在子线程中调用 post 或 sendMessage 方法,并通过 Handler 与主线程进行通信。
Looper Android|Android 消息机制 Handler 详解
文章图片
Android|Android 消息机制 Handler 详解
文章图片
Looper 是用于为线程运行消息循环的类。 线程默认情况下没有与它们相关的消息循环,因此必须创建一个,在要运行循环的线程中调用 prepare(),然后 loop() 使其处理消息,直到循环停止。 与消息循环的大多数交互是通过 Handler 类。
示例 这是执行 Looper 线程的一个典型例子,使用 prepare() 和 loop() 的分离来创建初始 Handler 来与 Looper 进行通信。
class LooperThread extends Thread { public Handler mHandler; @Override public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }

MessageQueue Android|Android 消息机制 Handler 详解
文章图片
Android|Android 消息机制 Handler 详解
文章图片
MessageQueue 类是持有由 Looper 发送的消息列表的低级别类。 消息不直接添加到 MessageQueue,而是通过与 Looper 关联的 Handler 对象添加。 可以使用 Looper.myQueue() 恢复到当前线程的 MessageQueue。
Android|Android 消息机制 Handler 详解
文章图片
消息机制 Android 应用启动时,会默认有一个主线程(UI 线程),在这个线程中会关联一个消息队列,所有的操作都会被封装成消息然后交给主线程来处理。获取消息的操作放在一个死循环中,保证主线程不会主动退出。这样程序相当于一直执行死循环,因此不会退出。
UI 线程的消息循环是在 ActivityThread.main 方法中创建的:
public static void main(String[] args) { ...... Process.setArgV0("
"); //1.创建消息循环Looper Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { //UI线程的Handler sMainThreadHandler = thread.getHandler(); } ...... //2.执行消息循环 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }

在子线程中完成耗时操作后,很多时候要更新 UI,常用的方式就是通过 Handler 将一个消息 Post 到 UI 线程中,然后在 Handler 中的 handleMessage() 方法中进行处理。有一点要注意,该 Handler 必须在主线程中创建。
一个简单的 Handler 示例:
public class MyHandler extends Handler {@Override public void handleMessage(Message msg) { //更新UI }MyHandler mMyHandler = new MyHandler(); //开启新线程 new Thread(){ @Override public void run() { //耗时操作 mMyHandler.handleMessage(123); } }.start(); }

一、Handler 如何关联消息队列以及线程呢? 每个 Handler 都会关联一个消息队列,消息队列被封装在 Looper 中,而每个 Looper 又会关联一个线程(Looper 通过 ThreadLocal 封装),于是每个消息队列关联一个线程。
public Handler() { this(null, false); }public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); //获取Looper if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); }mQueue = mLooper.mQueue; //获取消息队列 mCallback = callback; mAsynchronous = async; }

从以上 Handler 的构造函数可以看出,Handler 在内部通过Looper.myLooper() 来获取 Looper 对象,并且与之关联,最重要还获取了消息队列。
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }

myLooper() 方法中是通过 sThreadLocal.get() 来获取 Looper 对象。
那么 Looper 对象是什么时候存储在 sThreadLocal 中呢?
//设置UI线程的Looper public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }public static void prepare() { prepare(true); }//为当前线程设置一个 Looper,存储在 sThreadLocal 中 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)); }

答案就是在 prepareMainLooper() 方法中,这个方法又调用了 prepare() 方法,在 prepare() 中创建了一个 Looper 对象,并设置给 sThreadLocal。这样 Looper(消息队列)和线程就关联上了。所以,不同的线程不能访问对方的消息队列。
消息队列通过 Looper 与线程关联上了,Handler 与 Looper 关联上,因此三者就彼此关联了。就是因为 Handler 要与主线程的消息队列关联上,这样 handleMessage 才会在 UI 线程中执行,此时更新 UI 才是线程安全的。
二、创建 Looper 后,如何进行消息循环? 消息循环的建立就是通过 Looper.loop() 方法。
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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."); } final MessageQueue queue = me.mQueue; //1.获取消息队列...... for (; ; ) {//2.死循环,即消息循环 Message msg = queue.next(); // 3.获取消息 (might block) if (msg == null) { // No message indicates that the message queue is quitting. return; }...... try { msg.target.dispatchMessage(msg); //4.处理消息 ...... msg.recycleUnchecked(); //5.回收消息 } }

通过 Looper.prepare() 来创建 Looper 对象,消息队列封装在 Looper 对象中,Looper 对象保存在 sThreadLocal 中,通过 Looper.loop() 来执行消息循环,这两步通常成对出现。
三、消息如何被处理? 可以看到,Looper.loop() 中是通过第4步
msg.target.dispatchMessage(msg) 来处理消息的。
下面来看看 Message 的源码。
public final class Message implements Parcelable { ....../*package*/ Handler target; //target处理/*package*/ Runnable callback; //Runnable类型的callback// sometimes we store linked lists of these things /*package*/ Message next; //下一条消息,消息队列是链式存储的......

可以看到,target 是 Handler 类型,实际就是转了一圈,Handler 将消息投递给消息队列,消息队列又把消息分发给 Handler 来处理。
下面是 Handler 实际处理消息的源码
/** * Subclasses must implement this to receive messages. * 消息处理函数,子类必须覆写 */ public void handleMessage(Message msg) { }/** * Handle system messages here. * 分发消息 */ 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(); }

可以看到,dispatchMessage 只是一个分发方法,如果 Runnable 类型的 callback 为空,则执行 handleMessage() 方法来处理消息,通常我们需要覆写该方法,将更新 UI 的代码写在里面。如果 callback 不为空,则执行 handleCallback() 方法,该方法会调用 callback 的 run 方法。
其实就是 Handler 分发的两种类型,一般我们 post(Runnable callback) 则 callback 就不为空,当使用 sendMessage 时一般不会设置 callback,因此就执行 handleMessage 这个分支。
下面来看下 post() 方法的具体实现
public final boolean post(Runnable r) { returnsendMessageDelayed(getPostMessage(r), 0); }//将Runnable对象包装成Message对象 private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }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 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; //设置消息的target为当前的Handler对象 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); //将消息插入到消息队列 }

在 post(Runnable r) 时,会将 Runnable 对象包装成 Message 对象,并将 Runnable 对象设置给 Message 对象的 callback 字段,最后将该 Message 对象插入消息队列。
sendMessage 方法的实现也是类似。
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }

无论是 post 一个 Runnable 还是 sendMessage,都会调用 sendMessageDelayed(msg, time) 方法。
Handler 不断将消息追加到 MessageQueue 中,而 Looper 不断从 MessageQueue 中读取消息,并调用 Handler 的 dispatchMessage 消息,这样 Android 应用就运转起来。
四、在子线程中创建 Handler 为何会抛出异常? 先来看看下面这个代码
new Thread() { Handler mHandler = null; @Override public void run() { mHandler = new Handler(); } }.start();

这个代码是有问题的,会抛出 "Can't create handler inside thread that has not called Looper.prepare()" 异常,原因就出在 Handler 构造函数中。
public Handler() { this(null, false); }public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); //获取Looper if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); }mQueue = mLooper.mQueue; //获取消息队列 mCallback = callback; mAsynchronous = async;

抛出异常是因为该线程中 Looper 对象还没有创建,因此,sThreadLocal.get() 会返回 null。没有 Looper 就没有 MessageQueue,而 Handler 的原理就是要与 MessageQueue 建立关联。因此,创建 Handler 时 Looper 一定不能为空。
解决方案如下:
new Thread() { Handler mHandler = null; @Override public void run() { //1.为该线程创建Looper,并且会绑定到ThreadLocal中 Looper.prepare(); mHandler = new Handler(); //2.启动消息循环 Looper.loop(); } }.start();

【Android|Android 消息机制 Handler 详解】顺带一提,一个 Looper 可以被多个 Handler 共同使用,在 Handler 的构造方法里可以指定 Looper。
Android|Android 消息机制 Handler 详解
文章图片

    推荐阅读