Handler机制笔记,原文地址:https://mp.weixin.qq.com/s/7PAMm_FPrA0P3jf0tn3yy

Handler 如何运行

Handler角色分配

  • Handler中存在四种角色

Handler

  • Handler用来向Looper发送消息,在Looper处理到对应的消息时,Handler再对消息进行具体的处理。上层关键API为handleMessage(),由子类自行实现处理逻辑。

Looper

  • Looper运行在目标线程里,不断从消息队列MessageQueue读取消息,分配给Handler处理。Looper起到连接的作用,将来自不同渠道的消息,聚集在目标线程里处理。也因此Looper需要确保线程唯一。

MessageQueue

  • 存储消息对象Message,当Looper向MessageQueue获取消息,或Handler向其插入数据时,决定消息如何提取、如何存储。不仅如此,MessageQueue还维护与Native端的连接,也是解决Looper.loop() 阻塞问题的 Java 端的控制器。

Message

  • Message包含具体的消息数据,在成员变量target中保存了用来发送此消息的Handler引用。因此在消息获得这行时机时,能知道具体由哪一个Handler处理。此外静态成员变量sPool,则维护了消息缓存池以复用。

运行过程

  • 首先,需要构建消息对象。获取消息对象从Handler.obtainMessage()系列方法可以获取Message,这一系列的函数提供了相应对应于Message对象关键成员变量对应的函数参数,而无论使用哪一个方法获取,最终通过Message.obtain()获取具体的Message对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 缓存池
    private static Message sPool;
    // 缓存池当前容量
    private static int sPoolSize = 0;
    // 下一节点
    Message next;
    public static Message obtain() {
    // 确保同步
    synchronized (sPoolSync) {
    if (sPool != null) {
    // 缓存池不为空
    Message m = sPool;
    // 缓存池指向下一个Message节点
    sPool = m.next;
    // 从缓存池拿到的Message对象与缓存断开连接
    m.next = null;
    m.flags = 0; // clear in-use flag
    // 缓存池大小减一
    sPoolSize--;
    return m;
    }
    }
    // 缓存池没有可用对象,返回新的Message()
    return new Message();
    }
  • Message成员变量中存在类型为Message的next,可以看出Message为链表结构,而上面代码从缓存池里获取消息对象的过程可以用下图描述:

    image
    image
  • 创建出消息之后,通过Handler将消息发送到消息队列,发送方法有很多,不一一陈列。发送有两种:
  1. 将Message对象发送到Looper。利用sendMessage()
  2. 发送Runnable,通过getPostMessage()将Runnable包装在Message里,表现为成员变量callback
    1
    2
    3
    4
    5
    6
    7
    private static Message getPostMessage(Runnable r) {
    // 获取Message
    Message m = Message.obtain();
    // 记住Runnale,等消息获得执行时回调
    m.callback = r;
    return m;
    }
  • 不管哪种方式发送,最终消息队列MessageQueue只接受到了消息对象Message。而将消息加入到消息队列,最终通过enqueueMessage()加入。
  • 在将消息加入消息队列时,有时需要提供延迟信息delayTime,以期未来多久后执行,这个值存于 uptimeMillis。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // Message.target 记住 Handler 以明确是由哪一个Handler来处理这个消息的
    msg.target = this;
    if (mAsynchronous) {
    msg.setAsynchronous(true);
    }
    // 消息入队
    return queue.enqueueMessage(msg, uptimeMillis);
    }
  • 之后,等待Looper轮询从消息队列中读取消息进行处理。见Looper.loop()。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public static void loop() {
    // 拿到Looper
    final Looper me = myLooper();
    if (me == null) {
    // 没调用prepare初始化Looper,报错
    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 拿到消息队列
    final MessageQueue queue = me.mQueue;
    ......
    for (;;) {
    // 从消息队列取出下一个信息
    Message msg = queue.next();
    if (msg == null) {
    // 消息为空,返回
    return;
    }
    .......
    try {
    // 分发消息到Handler
    msg.target.dispatchMessage(msg);
    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
    }
    // 消息回收,放入缓存池
    msg.recycleUnchecked();
    }
  • Looper从MessageQueue里取出Message,Message.target则是具体的Hander,Handler.dispatchMessage()将触发具体分配逻辑。此后,将Message回收,放入缓存池。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    // 这个情况说明了本次消息为Runnable,触发Runnable.run()
    handleCallback(msg);
    } else {
    if (mCallback != null) {
    // 指定了Handler的mCallback
    if (mCallback.handleMessage(msg)) {
    return;
    }
    }
    // 普通消息处理
    handleMessage(msg);
    }
    }
  • Handler分配消息分三种情况:

  1. 可以通过Handler发送Runnable消息到消息队列,因此handleCallback()处理这种情况
  2. 可以给Handler设置Callback,当分配消息给Handler时,Callback可以优先处理此消息,如果Callback.handleMessage()返回了true,不再执行Handler.handleMessage()
  3. Handler.handleMessage()处理具体逻辑
  • 回收Message则是通过Message.recycleUnchecked()。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void recycleUnchecked() {
    // 这里是将Message各种属性重置操作
    ......
    synchronized (sPoolSync) {
    if (sPoolSize < MAX_POOL_SIZE) {
    // 缓存池还能装下,回收到缓存池
    // 下面操作将此Message加入到缓存池头部
    next = sPool;
    sPool = this;
    sPoolSize++;
    }
    }
    }
  • 通过上面的分析,Handler的运行如下图:

    image
    image
  1. Handler 从缓存池获取Message,发送到MessageQueue
  2. Looper不断从MessageQueue读取消息,通过Message.target.dispatchMessage()触发Handler处理逻辑
  3. 回收Message到缓存池

Java端与Native端建立连接

  • 实际上,不仅仅是Java端存在Handler机制,在Native端同样存在Handler机制。他们通过MessageQueue建立了连接。
  • 一般来说,Looper通过prepare()进行初始化。

    1
    2
    3
    4
    5
    6
    7
    8
    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));
    }
  • 在实例化Looper时,需要确保Looper在线程里是唯一的。Handler知道自己的具体Looper对象,而Looper运行在具体的线程里并在此线程里处理消息。这也是为什么Looper能达到切换线程的目的。Looper线程唯一需要ThreadLocal来确保,ThreadLocal的原理,简单来说Thread里有类型为ThreadLocalMap的成员threadLocals,通过ThreadLocal能将相应对象放入threadLocals里通过K/V存储,如此能保证变量在线程范围内存储,其中Key为ThreadLocal< T > 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private Looper(boolean quitAllowed) {
    // 初始化MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    // 记住当前线程
    mThread = Thread.currentThread();
    }
    MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    // 与Native建立连接
    mPtr = nativeInit();
    }
  • 在MessageQueue创建时,通过native方法nativeInit()与Native端建立了连接,mPtr为long型变量,存储一个地址。方法实现文件位于frameworks/base/core/jni/android_os_MessageQueue.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
    jniThrowRuntimeException(env, "Unable to allocate native queue");
    return 0;
    }
    nativeMessageQueue->incStrong(env);
    // 返回给Java层的mPtr, NativeMessageQueue地址值
    return reinterpret_cast<jlong>(nativeMessageQueue);
    }
    NativeMessageQueue::NativeMessageQueue() :
    mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    // 检查Looper 是否创建
    if (mLooper == NULL) {
    mLooper = new Looper(false);
    // 确保Looper唯一
    Looper::setForThread(mLooper);
    }
    }
  • 在Native端创建了NativeMessageQueue,同样也创建了Native端的Looper。在创建NativeMessageQueue后,将它的地址值返回给了Java层MessageQueue.mPtr。实际上,Native端Looper实例化时做了更多事情。Nativ端Looper文件位于system/core/libutils/Looper.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    Looper::Looper(bool allowNonCallbacks) :
    mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
    mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
    mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    // 添加到epoll的文件描述符,线程唤醒事件的fd
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
    strerror(errno));
    AutoMutex _l(mLock);
    rebuildEpollLocked();
    }
    void Looper::rebuildEpollLocked() {
    .....
    // Allocate the new epoll instance and register the wake pipe.
    // 创建epolle实例,并注册wake管道
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
    struct epoll_event eventItem;
    // 清空,把未使用的数据区域进行置0操作
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    // 监听可读事件
    eventItem.events = EPOLLIN;
    // 设置作为唤醒评判的fd
    eventItem.data.fd = mWakeEventFd;
    // 将唤醒事件(mWakeEventFd)添加到epoll实例,意为放置一个唤醒机制
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
    strerror(errno));
    // 添加各种事件的fd到epoll实例,如键盘、传感器输入等
    for (size_t i = 0; i < mRequests.size(); i++) {
    const Request& request = mRequests.valueAt(i);
    struct epoll_event eventItem;
    request.initEventItem(&eventItem);
    int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
    if (epollResult < 0) {
    ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
    request.fd, strerror(errno));
    }
    }
    }

如何理解epoll机制?

  • 文件、socket、pipe(管道)等可以进行I/O操作的对象可以视为流。既然是I/O操作,则有read端读入数据,有write端写入数据。但是两端并不知道对方进行操作的时机。而epoll则能观察到哪个流发生了了I/O事件,并进行通知。
  • 这个过程,就好比你在等快递,但你不知道快递什么时候来,那这时你可以去睡觉,因为你知道快递送来时一定会打个电话叫醒你,让你拿快递,接着做你想的事情。
  • epoll有效地降低了CPU的使用,在线程空间时令其休眠,等有事件到来时再讲它唤醒。
  • 在知道了epoll之后,再来看上面的代码,就可以理解了。在Native端创建Looper时,会创建用来唤醒线程的fd —— mWakeEventFd,创建epoll实例并注册管道,清空管道数据,监听可读事件。当有数据写入mWakeEventFd描述的文件时,epoll能监听到此事件,并通知将目标线程唤醒。
  • 在Java端MessageQueue.mPrt存储了Native端NativeMassageQueue的地址,可以利用NativeMassageQueue享用此机制。

发送数据的具体过程

  • Handler发送消息时,最终通过MessageQueue.enqueueMessage向消息队列中插入消息,下面为具体代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    boolean enqueueMessage(Message msg, long when) {
    ......
    synchronized (this) {
    ......
    // 记录消息处理的时间
    msg.when = when;
    Message p = mMessages;
    // 唤醒线程的标志位
    boolean needWake;
    if (p == null || when == 0 || when < p.when) {
    // 这里三种情况:
    // 1、目标消息队列是空队列
    // 2、插入的消息处理时间等于0
    // 3、插入的消息处理时间小于保存在消息队列头的消息处理时间
    // 这三种情况都插入列表头
    msg.next = p;
    mMessages = msg;
    // mBlocked 表示当前线程是否睡眠
    needWake = mBlocked;
    } else {
    // 这里则说明消息处理时间大于消息列表头的处理时间,因此需要找到合适的插入位置
    needWake = mBlocked && p.target == null && msg.isAsynchronous();
    Message prev;
    // 这里的循环是找到消息的插入位置
    for (;;) {
    prev = p;
    p = p.next;
    // 到链表尾,或处理时间早于p的时间
    if (p == null || when < p.when) {
    break;
    }
    if (needWake && p.isAsynchronous()) {
    // 如果插入的消息在目标队列中间,是不需要检查改变线程唤醒状态的
    needWake = false;
    }
    }
    // 插入到消息队列
    msg.next = p;
    prev.next = msg;
    }
    if (needWake) {
    // 唤醒线程
    nativeWake(mPtr);
    }
    }
    return true;
    }
  • 消息队列里的消息也是以链表形式存储,存储顺序则按照处理的时间顺序。那么在向消息队列里插入数据时,存在四种情况:

    • 目标消息队列是空队列
    • 插入的消息处理时间等于0
    • 插入的消息处理时间小于保存在消息队列头的消息处理时间
    • 插入的消息处理时间大于消息队列头的消息处理时间
  • 前三种情况,将消息插入消息队列头的位置,在这种情况下,因为不能保证当前消息是否达到了可以处理的状态,且如果此时线程是睡眠的,则需要调用nativeWake()将其线程唤醒。后一种情况,则需要找到消息的插入位置,因不影响线程状态而需要改变线程状态。插入消息如图:
    image
    image
  • mPtr保存了NativeMessageQueue的地址,所以Native可以知道具体操作的NativeMessageQueue,当前用它来唤醒线程,实际调用链为MessageQueue.cpp.nativeWake()MessageQueue.cpp.wake()Looper.cpp.wake()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void Looper::wake() {
    #if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
    #endif
    uint64_t inc = 1;
    // 向管道写入一个新数据,这样管道因为发生了IO事件被唤醒
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
    if (errno != EAGAIN) {
    LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
    mWakeEventFd, strerror(errno));
    }
    }
    }
  • 实现也简单,向mWakeEventFd文件里写入一个数据,根据epoll机制监听到此次I/O事件,将线程唤醒。

消息读取

  • Looper不断从MessageQueue读取消息进行处理,通过MessageQueue.next()进行读取。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
    // 获取NativeMessageQueue地址失败,无法正常使用epoll机制
    return null;
    }
    // 用来保存注册到消息队列中的空闲消息处理器(IdleHandler)的个数
    int pendingIdleHandlerCount = -1;
    // 如果这个变量等于0,表示即便消息队列中没有新的消息需要处理,当前
    // 线程也不要进入睡眠等待状态。如果值等于-1,那么就表示当消息队列中没有新的消息
    // 需要处理时,当前线程需要无限地处于休眠等待状态,直到它被其它线程唤醒为止
    int nextPollTimeoutMillis = 0;
    for (;;) {
    ......
    // 检查当前线程的消息队列中是否有新的消息需要处理,尝试进入休眠
    nativePollOnce(ptr, nextPollTimeoutMillis);
    synchronized (this) {
    // 当前时间
    final long now = SystemClock.uptimeMillis();
    Message prevMsg = null;
    // mMessages 表示当前线程需要处理的消息
    Message msg = mMessages;
    if (msg != null && msg.target == null) {
    // 找到有效的Message
    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;
    }
    ......
    if (pendingIdleHandlerCount < 0
    && (mMessages == null || now < mMessages.when)) {
    // 获取IdleHandler数
    pendingIdleHandlerCount = mIdleHandlers.size();
    }
    if (pendingIdleHandlerCount <= 0) {
    // 没有IdleHandler需要处理,可直接进入休眠
    mBlocked = true;
    continue;
    }
    if (mPendingIdleHandlers == null) {
    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
    }
    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    }
    // 如果没有更多要进行处理的消息,在休眠之前,发送线程空闲消息给已注册到消息队列中的IdleHandler对象来处理
    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);
    }
    }
    }
    // 重置IdleHandler数量
    pendingIdleHandlerCount = 0;
    /**
    * 这里置0,表示下一次循环不能马上进入休眠状态,因为IdleHandler在处理事件的时间里,
    * 有可能有新的消息发送来过来,需要重新检查。
    */
    nextPollTimeoutMillis = 0;
    }
    }
  • 分为两种情况处理:

    • 取到消息Message时:
      • 需要查看当前时间是否达到了Message处理的时间,如果达到了则返回,改变mMessages指向下一消息。如果没达到,则计算要达到处理的时间,还需要休眠多久,并进行休眠。
    • 没有更多Message时:
      • 当消息队列里没有消息时,则会检查是否有IdleHandler需要处理。在Handler机制里,允许添加一些事件,在线程空闲时进行处理,表现为IdleHandler,可以通过MessageQueue.addIdleHandler添加。当有IdleHandler需要处理,则在IdleHandler处理完后,线程不能马上进入休眠状态,在此期间可能已有新消息加入消息队列,需要重新做检查。如果没有IdleHandler,则可以进入休眠。
  • 线程休眠调用链为NativeMessageQueue.nativePollOnce()NativeMessageQueue.pollOnce()Looper.pollOnce()Looper.pollInner()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    int Looper::pollInner(int timeoutMillis) {
    ......
    // 这个是用来监听实例化时创建的epoll实例的文件描述符的IO读写事件
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    // 如果没有事件,进入休眠,timeoutMillis为休眠事件
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ......
    /**
    * 检测是哪一个文件描述符发生了IO读写事件
    */
    for (int i = 0; i < eventCount; i++) {
    int fd = eventItems[i].data.fd;
    uint32_t epollEvents = eventItems[i].events;
    if (fd == mWakeEventFd) {
    if (epollEvents & EPOLLIN) {
    // 如果文件描述符为mWakeEventFd,并且读写事件类型为EPOLLIN,说明
    // 当前线程所关联的一个管道被写入了一个新的数据
    // 唤醒
    awoken();
    }
    }
    ......
    }
    }
  • Java层提供了线程休眠时间timeoutMillis,通过epoll_wait()让线程进行休眠。当线程被唤醒后,查看文件描述符,如果为mWakeEventFd并且为I/O事件,则说明当前线程所关联的一个管道被写入了一个新的数据,通过awoken()处理。当前线程已是唤醒状态,awoken()则是将管道中的数据读出达到清理目的,但并不关心数据什么。核心目的是唤醒线程。

总结

  • Handler机制更具体的原理如图:
    image
    image
  1. Looper通过prepare()创建,借助ThreadLocal保证线程唯一,如果没有进行prepare(),调用Loop()会抛出异常;
  2. Looper在实例化时创建MessageQueue,MessageQueue与NativeMessageQueue建立连接,NativeMessageQueue存储地址存于MessageQueue.mPtr。Native端也建立了Handler机制,使用epoll机制。Java端借由NativeMessageQueue能达到使用epoll机制的目的;
  3. 从Message缓存里获取Message,缓存为链表存储,从头出取出,并且Message在回收时也是插入头部。如果存缓存里取不到,则新建;
  4. Handler向MessageQueue插入消息,如果消息插入消息队列头部,需要唤醒线程;如果插入消息队列中,无需改变线程状态;
  5. Looper.loop() 不断从消息队列获取消息,消息队列获取消息时会出现两种情况。如果取到消息,但没达到处理时间,则让线程休眠;如果没有更多消息,则在处理IdleHandler事后,在考虑让线程进入休眠;
  6. Message达到了可处理状态,则有Handler处理,处理时考虑三种情况,消息内容为Runnable时、设置了Handle.Callback时、普通消息时,对应调用为Message.callback.run()Callback.handleMessage()Handler.handleMessage();
  7. 从Handler机制里,epoll可以简单理解为,当Handler机制没有消息要处理时,让线程进入休眠,当Handler机制有消息要处理时,将线程唤起。通过Native端监听mWakeEventFd的I/O事件实现。

疑问点

Handler如何保证运行在目标线程

  • Looper在实例化时通过ThreadLocal保证线程唯一。Looper运行在目标线程中,接收Handler发送的消息并进行处理。Message创建时与具体的Handler进行了关联,因此能知道由哪一个Handler进行相应。

Handler容易造成内存泄漏的原因

  • Message.target存有Handler的引用,以知道自身由哪一个Handler来处理。因此,当Handler为非静态内部类、或持有关键对象的其它表现形式时(如Activity常表现为Context),就引用了其它外部对象。当Message得不到处理时,被Handler持有的外部对象会一直处于内存泄漏状态。

loop()为什么不会阻塞,CPU为什么不会忙等

  • 通过epoll机制监听文件I/O时间,在有Message需要处理时,写入数据以唤醒线程;在没有Message要处理时,让线程进入休眠状态。

MessageQueue如何存储

  • 以链表存储,MessageQueue.mMessages指向头节点。

Message如何缓存

  • 以链表缓存,取出时从头部取出,回收时插入头部。

什么是线程空闲消息

  • Handler提供的一种机制,允许添加IdleHandler事件。并在没有更多Message要处理,要进入休眠前,让IdleHandler做具体事情,也就是线程空间时处理的事件。

子线程如何使用Handler机制

  • 只要保证在子线程先执行Looper.prepare()再使用Looper.Loop()即可,但实际应用场景不多。顺便提一句,主线程初始化Looper操作在ActivityTread.main()里触发,简单了解即可。