跳到主要内容

Handler 介绍

定义

一套 Android 消息传递机制 / 异步通信机制

背景

  • 在 Android 开发中,UI 线程 (主线程) 负责处理用户界面的交互和更新。
  • 如果 UI 线程被长时间阻塞或执行耗时操作,将会导致用户界面的响应性降低,用户体验很差。
  • 如果在主线程进行大量耗时操作,没有响应用户输入事件 (如点击、滑动等) 时,Android 系统会认为应用程序无响应,并弹出 ANR (Application Not Responding) 对话框提醒用户。这个时间阈值通常为 5 秒。

问题

  • UI 线程 (主线程) 不能进行大量耗时的工作,需要使用子线程 (工作线程) 处理。
  • 如果工作线程直接尝试更新 UI 组件,就会违反 Android 的线程安全规定,可能会导致应用程序崩溃或界面异常。

解决方案

使用 Handler 机制进行线程间通信:

  • 启动一个子线程 (工作线程) 进行耗时的任务处理,可以向 UI 线程的 Handler 发送消息。
  • UI 线程的 Handler 收到消息后,在主线程中更新 UI 组件。

这样可以保证 UI 操作在主线程中执行,从而避免线程安全问题。

和真实世界类比

下图说明了子线程进行耗时操作,并且更新主线程的流程。

流程Handler 类流程真实世界类比
1创建 Looper 对象并启动创建一个工厂车间的传送带系统
2创建 MessageQueue 对象准备用于存放货物的货物槽链
3创建 Handler 对象并和 Looper 对象关联为传送带指派一名专门的处理人员
4在工作线程中执行耗时操作在另一个车间进行耗时的生产加工
5将需要通知主线程的更新信息封装在 Message 对象中将需要通知处理人员的货物信息打包到货物槽
6将 Message 对象添加到 MessageQueue 中将打包好的货物放入货物槽
7Looper 不断循环读取 MessageQueue 中的 Message传送带不断运转,读取其中的货物槽的信息
8Looper 取出 Message 对象,并分发给关联的 Handler 处理处理人员取出货物槽,交给对应的处理人员进行处理
9Handler 的 handleMessage() 方法中更新 UI 组件处理人员根据货物槽内的信息,执行相应的处理操作

Handler / Looper / Thread 的关系

  • 一个线程最多只有一个 Looper 对象,一个 Looper 对象只和一个线程关联
    • 主线程创建 Looper:Android 主线程都是 Activity,在 ActivityThread 内会自动创建
    • 子线程创建 Looper:
      • 继承于 Thread,在 run() 里面调用 Looper.prepare() 和 Looper.loop()
      • 继承于 HandlerThread,自动创建
  • 一个 Looper 对象中必须有且仅有一个 MessageQueue 对象
  • 一个 Looper 对象可以关联 0 到多个 Handler 对象

Handler 源码分析

Looper 对象创建 & MessageQueue 对象创建

创建 Looper 时会同步创建 MessageQueue 对象

主线程 Looper 的创建

  • ActivityThread.main() 调用 Looper.prepareMainLooper() 和 Looper.loop()

    /**
    * 源码分析:Looper.prepareMainLooper()
    * 作用:为主线程(UI 线程)创建 1 个循环器对象(Looper),同时也生成了 1 个消息队列对象(MessageQueue)
    * 注:该方法在主线程(UI 线程)创建时自动调用,即 主线程的 Looper 对象自动生成,不需手动生成
    */

    // 在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫 UI 线程)
    // 创建时,会自动调用 ActivityThread 的1个静态的 main() 方法 = 应用程序的入口
    // main() 内则会调用 Looper.prepareMainLooper() 为主线程生成 1 个 Looper 对象

    public static void main(String[] args) {
    ...

    // 1. 为主线程创建 1 个 Looper 对象,同时生成 1 个消息队列对象(MessageQueue)
    // 方法逻辑类似 Looper.prepare()
    // 注:prepare():为子线程中创建 1 个 Looper 对象
    Looper.prepareMainLooper();
    ...

    // 2. 创建主线程
    ActivityThread thread = new ActivityThread();
    ...

    // 3. 自动开启消息循环
    Looper.loop();

    }
  • Looper.prepareMainLooper():在主线程中创建 1 个 Looper 对象 (自动生成)

        /**
    * Initialize the current thread as a looper, marking it as an
    * application's main looper. See also: {@link #prepare()}
    *
    * @deprecated The main looper for your application is created by the Android environment,
    * so you should never need to call this function yourself.
    */
    @Deprecated
    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.myLooper()
      /**
      * Return the Looper object associated with the current thread. Returns
      * null if the calling thread is not associated with a Looper.
      */
      public static @Nullable Looper myLooper() {
      return sThreadLocal.get();
      }
      • sThreadLocal
        // sThreadLocal.get() will return null unless you've called prepare().
        @UnsupportedAppUsage
        static final ThreadLocal<Looper> sThreadLocal= new ThreadLocal<Looper>();

子线程 Looper 的创建

  • Looper.prepare():在子线程中创建 1 个 Looper 对象 (手动创建)

        /** Initialize the current thread as a looper.
    * This gives you a chance to create handlers that then reference
    * this looper, before actually starting the loop. Be sure to call
    * {@link #loop()} after calling this method, and end it by calling
    * {@link #quit()}.
    */
    public static void prepare() {
    prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
    // 1. 判断 sThreadLocal 是否为 null,否则抛出异常
    // 即 Looper.prepare() 方法不能被调用两次, 1 个线程中只能对应 1 个 Looper 实例
    // 注:sThreadLocal 为 ThreadLocal 对象,用于存储线程的变量
    if (sThreadLocal.get() != null) {
    throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
    }
    • Looper.Looper()
          private Looper(boolean quitAllowed) {
      // 创建 1 个 Looper 实例时,会自动创建 1 个与之配对的 MessageQueue
      mQueue = new MessageQueue(quitAllowed);
      mThread = Thread.currentThread();
      }

Handler 对象创建

  • Handler 对象创建

        private Handler mHandler = new Handler(Looper.myLooper()){
    };
    • Handler() 创建时,关联 Looper 对象和 Queue 对象。

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

            final Looper mLooper;
        final MessageQueue mQueue;

        @UnsupportedAppUsage
        public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
        }

Message 发送

  • Handler.sendMessageAtTime(Message msg,long uptimeMillis):调用 enqueueMessage()

     /**
    * 分析:sendMessageAtTime(Message msg, long uptimeMillis)
    * 作用:在指定时间发出消息,消息会加入到消息队列 MessageQueue
    */

    /**
    * Enqueue a message into the message queue after all pending messages
    * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
    * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
    * Time spent in deep sleep will add an additional delay to execution.
    * You will receive it in {@link #handleMessage}, in the thread attached
    * to this handler.
    *
    * @param uptimeMillis The absolute time at which the message should be
    * delivered, using the
    * {@link android.os.SystemClock#uptimeMillis} time-base.
    *
    * @return Returns true if the message was successfully placed in to the
    * message queue. Returns false on failure, usually because the
    * looper processing the message queue is exiting. Note that a
    * result of true does not mean the message will be processed -- if
    * the looper is quit before the delivery time of the message
    * occurs then the message will be dropped.
    */
    public boolean sendMessageAtTime(@NonNull 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);
    }
    • Handler.enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

      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);
      }
  • MessageQueue.enqueueMessage(Message msg,long when):将 Message 对象加入消息队列

     /**
    * 分析:MessageQueue.enqueueMessage()
    * 作用:入队消息,即将新的消息加入消息队列
    */

    // mMessages 为链表结构,头部为优先执行
    // 即优先执行 mMessages,执行完之后 mMessages = mMessages.next,直到 mMessages 为空为止
    Message mMessages;
    ...

    boolean enqueueMessage(Message msg, long when) {
    // msg.target 即目标 Handler,如果为 null 则抛出异常
    if (msg.target == null) {
    throw new IllegalArgumentException("Message must have a target.");
    }

    // 可能出现存在多个线程同时插入消息多情况,用 synchronized 同步
    synchronized (this) {
    if (msg.isInUse()) {
    throw new IllegalStateException(msg + " This message is already in use.");
    }
    ...

    // msg:当前(需要加入消息队列的)消息
    msg.markInUse();
    msg.when = when;
    // p (mMessages):指针,指向当前队列的头部
    Message p = mMessages;
    boolean needWake;
    // 如果以下出现情况之一:当前队列为空;当前 msg 需要马上执行;当前 msg 需要执行的时间 < 头部消息需要执行的时间,则将当前消息插入队列头部;反止插入队列头部之后(中间或尾部)。
    if (p == null || when == 0 || when < p.when) {
    // 将当前消息插入队列头部
    // New head, wake up the event queue if blocked.
    msg.next = p;
    mMessages = msg;
    needWake = mBlocked;
    } else {
    // Inserted within the middle of the queue. Usually we don't have to wake
    // up the event queue unless there is a barrier at the head of the queue
    // and the message is the earliest asynchronous message in the queue.
    // 是否需要队列保持唤醒状态
    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;
    }

Message 分发和 Handler 处理

  • Looper.loop():取出 Looper 中的消息队列,使用 MessageQueue.next() 无限循环取出 msg 并发送到对应的 handler

    /**
    * 作用:消息循环,即从消息队列中获取消息、分发消息到 Handler
    * 特别注意:
    * a. 主线程的消息循环不允许退出,即无限循环
    * b. 子线程的消息循环允许退出:调用消息队列 MessageQueue 的 quit()
    */

    /**
    * Run the message queue in this thread. Be sure to call
    * {@link #quit()} to end the loop.
    */
    public static void loop() {

    // 1. 获取当前Looper的消息队列
    // myLooper()作用:返回 sThreadLocal 存储的 Looper 实例;若为 null 则抛出异常
    final Looper me = myLooper();


    // 若为 null 则抛出异常,即 loop()执行前必须执行 prepare() 创建 Looper 实例
    if (me == null) {
    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    ...

    me.mInLoop = true;
    // 获取Looper实例中的消息队列对象 (MessageQueue)
    final MessageQueue queue = me.mQueue;

    // 2. 消息循环(for 无限循环)
    for (;;) {
    // 2.1 从消息队列取出消息 msg,若取出消息为空则线程阻塞
    Message msg = queue.next(); // might block
    if (msg == null) {
    // No message indicates that the message queue is quitting.
    return;
    }

    try {
    // 2.2 把消息 msg 派发给消息对象 msg.target(实际是 handler 对象)
    msg.target.dispatchMessage(msg);
    ...
    } ...
    ...

    // 3. 释放资源
    msg.recycleUnchecked();
    }
    }
  • MessageQueue.next():从消息队列中取出下一个 Message 对象

     /**
    * 分析:MessageQueue.next()
    * 作用:出队消息,即从消息队列中移出头部消息
    */

    // mMessages 为链表结构,头部为优先执行
    // 即优先执行 mMessages,执行完之后 mMessages = mMessages.next,直到 mMessages 为空为止
    Message mMessages;
    ...

    Message next() {
    // 该参数用于确定消息队列中是否还有消息
    // 从而决定消息队列应处于出队消息状态 or 等待状态
    int nextPollTimeoutMillis = 0;

    for (;;) {
    if (nextPollTimeoutMillis != 0) {
    Binder.flushPendingCommands();
    }

    // nativePollOnce 方法在 native 层,若是 nextPollTimeoutMillis 为 -1,此时消息队列处于等待状态 
    nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {

    final long now = SystemClock.uptimeMillis();
    Message prevMsg = null;
    // 从消息队列中取出头部消息对象作为当前的 msg
    Message msg = mMessages;

    if (msg != null) {
    // 如果当前 msg 时间大于当前时间,重新计算 nextPollTimeoutMillis 并设置超时唤醒
    if (now < msg.when) {
    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
    } else {
    // 如果当前 msg 时间等于或者小于当前时间,取出当前 msg
    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
    // 下次循环时,消息队列则处于等待状态
    nextPollTimeoutMillis = -1;
    }
    ...
    }
    ...
    }
    }
  • Handler.dispatchMessage():将消息分发给接收者/回调方,由接收者/回调方进行相应处理

     /**
    * 分析:dispatchMessage(Message msg)
    * 作用:派发消息到对应的 Handler 实例,根据传入的 msg 作出对应的操作
    */

    /**
    * Handle system messages here.
    */
    public void dispatchMessage(@NonNull Message msg) {
    // 若 msg.callback 属性不为空,则代表使用了 post(Runnable r) 发送消息
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    // 若 mCallback 不为空,则代表使用了 Handler(Looper looper, Callback callback) 传入了 callback 参数;之后调用 callback 自己实现的 handleMessage() 方法(必须进行 override 重写,返回 true 或 false)
    if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
    return;
    }
    }
    // 若msg.callback属性为空,则代表使用了 sendMessage(Message msg)发送消息;之后调用 handleMessage(msg)(一般都会进行 override 重写)
    handleMessage(msg);
    }
    }