Android 的消息机制

Android 的消息机制主要是指 Handler 的运行机制,Handler 的运行需要底层的 MessageQueue 和 Looper 来支持。

MessageQueue 是一个消息的存储单元,Looper 以无限循环的形式去查找是否有新的消息,有的话就处理,没有就一直等待。Looper 中有一个特殊的概念 - ThreadLocal,它并不是线程,其作用是可以在每个线程中存储数据。Handler 可以通过 ThreadLocal 轻松获取每个线程的 Looper。

线程默认是没有 Looper 的,如果需要使用 Handler 就必须为线程创建 Looper。Android 主线程也叫 UI 线程,确切的说应该是 ActivityThread,其被创建时就会初始化 Looper,这就是主线程默认可以使用 Handler 的原因。

Android 的消息机制概述

  1. Android 系统不允许子线程更新 UI 是因为 UI 控件不是线程安全的。
  2. Handler 创建时会采用当前所在线程的 Looper 来构建内部消息循环系统,如果当前线程没有 Looper,则会报错。
  3. Handler 的 post 方法将一个 Runnable 投递到 Handler 内部的 Looper 中去处理,其实最终都是通过 send 方法来完成的。
  4. Handler 的 send 方法调用时,会调用 MessageQueue 的 enqueueMessage 方法将这个消息放入消息队列,然后 Looper 就发现有新消息,接着处理这个消息,最终消息中的 Runnable 或者 Handler 的 handleMessage 方法就会被调用,这样一来就实现了线程切换。

Android 的消息机制分析

ThreadLocal 的工作原理
  1. ThreadLocal 是一个线程内部的数据存储类,存储数据后,只有在指定线程才能获取数据,其他线程无法获取。
  2. 当某些数据是以线程为作用域并且不同线程具有不同的数据副本时可以使用 ThreadLocal。
  3. 不同线程中访问同一个 ThreadLocal 的 set 和 get 方法,它们对 ThreadLocal 所做的读写操作仅限于各自线程内部,这就是互不干扰的原因。
消息队列的工作原理
  1. 消息队列在 Android 中指的是 MessageQueue,MessageQueue 主要包含两个操作:插入(enqueueMessage)和读取(next)。
  2. next 读取消息的同时会将其从消息队列中删除。
  3. MessageQueue 的内部实现并不是队列,而是通过一个单链表的数据结构来维护消息列表。
  4. next 方法是一个无线循环的方法,没有消息时一直阻塞,当有新消息到来,next 会返回这条消息并将其从单链表中移除。
Looper 的工作原理
  1. Looper 不停地从 MessageQueue 中查看是否有新的消息,如果有就立即处理,否则就一致阻塞在那里。
  2. 为线程创建 Looper:

    1
    2
    3
    4
    5
    6
    7
    8
    new Thread() {
    @Override
    public void run() {
    Looper.prepare(); // 创建 Looper
    Handler handler = new Handler();
    Looper.loop(); // 开启消息循环
    }
    }.start();
  3. Looper.prepareMainLooper() 可以给主线程 ActivityThread 创建 Looper。

  4. 获取主线程 Looper 可以通过 Handler handler = new Handler(Looper.getMainLooper());
  5. Looper 提供了两个方法退出 quitquitSafely,退出后 Handler 发送消息会失败。
  6. 如果手动为子线程创建了 Looper,那么所有事情完成后应该调用 quit 方法来终止消息循环,否则线程会一直处于等待状态。
Handler 的工作原理
  1. Handler 的工作主要包括消息的发送(postsend)和接收过程。
  2. Handler 发送消息的过程仅仅是向消息队列中插入了一条消息,MessageQueue 的 next 方法就会返回这条消息给 Looper,Looper 收到消息后就开始处理了,最终消息由 Looper 交由 Handler 处理,即 Handler 的 dispatchMessage 的方法会被回调。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
    return;
    }
    }
    handleMessage(msg);
    }
    }

    image

主线程的消息循环

  1. Android 主线程 ActivityThread,入口方法为 main,在 main 方法中通过 Looper.prepareMainLooper() 来创建主线程 Looper 和 MessageQueue,并通过 Looper.loop() 开启主线程消息循环。
  2. ActivityThread 定义了一个内部类 ActivityThread.H 来和消息队列进行交互。
  3. ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信,ApplicationThread 会向 H 发送消息,H 收到消息会将 ApplicationThread 的逻辑切换到 ActivityThread 主线程去执行。