Handler

Posted by ooftf on April 12, 2021

运行机制包括

  • Handler
    消息处理
    持有 Looper、MessageQueue 对象
  • Message
    消息实体,用于存放需要处理的数据
    持有 Handler 对象
  • MessageQueue
    消息队列
    持有 Message
    已经发送的消息,存放在 MessageQueue 这个消息队列上,等在 Looper 取出
  • Looper
    消息循环体
    持有 MeeageQueue
    一直循环取消息,促使Handler 执行 handleMessage

Handler.postDelay 是准时的吗

不准时,因为内部有锁,有锁的地方就有竞争无法做到准时

如果保证一个线程只有一个Looper

1
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

静态变量保证只有一个 sThreadLocal
用 ThreadLocal 来保证每一个线程都有一个 Looper

Android中存在那么多Messager为什么没有产生内存抖动

MessagePool 存放头是,链表结构 message执行完后并没有释放,而是将message的内容置空添加到MessagePool,长度最大50

Looper

首先整个App程序的入口在 ActivityThread 的 main 方法中

1
2
3
4
5
6
7
public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    Looper.loop();
    ...
}

Hanlder 机制流程

  1. Message obtain()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    

    sPool 是一个Message 链表的头节点,当 Message 使用完的时候,就会添加到 sPool 链表头部,调用 obtain 从链表中取出头节点,如果链表为空创建新 Message
    与之对应的 recycle() 调用了 recycleUnchecked()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
        void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = UID_NONE;
            workSourceUid = UID_NONE;
            when = 0;
            target = null;
            callback = null;
            data = null;
       
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    
  2. 创建 Handler()
    Handler() 如果不指定 Looper 默认是当前线程的 Looper
  3. 发送消息 handler.sendMessage(message) 最终会执行到 handler.enqueueMessage
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
        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);
        }
    
  4. MessageQueue.enqueueMessage 将 Message 添加到 MessageQueue
    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
    
    boolean enqueueMessage(Message msg, long when) {
            ...
            synchronized (this) {
                ...
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                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 就插入到链表头部;如果是延迟消息就遍历列表根据执行时间插入到对应的位置。由插入规则可知,这是一个根据执行时间排序的链表

  5. 等待 Looper.loop 执行到 Message
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
       public static void loop() {
            final Looper me = myLooper();
            final MessageQueue queue = me.mQueue;
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                ...
                msg.target.dispatchMessage(msg);
                ...
                msg.recycleUnchecked();
            }
        }
    

    由代码可知,这是一个循环,不断的从 MessageQueue.next() 中获取到 message 并交由 Handler 执行,执行完毕会调用 recycleUnchecked 将自己添加到回收池中

  6. MessageQueue.next() 获取 Message
    if (msg == null) {return;} 这句代码意思是如果 msg 为 null 就会退出 Looper ,由前文可知如果主线程 Looper 退出,整个程序就会退出,显然不符合App的实际运行情况,也就是说主线程 msg 除非应用退出否则不可能为 null 。实际情况是所有线程的 Looper 除非主动退出,msg 都不会为 null 。 确保 msg 不为 null 的点在于 queue.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
    
        Message next() {
            ...
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
       
                nativePollOnce(ptr, nextPollTimeoutMillis);
       
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    // 如果msg.target == null 表示这是一个屏障消息,用于触发屏障机制,当触发屏障机制只会执行异步方法
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        // 找到异步消息
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                         
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
       
                    ...
                }
       
                ...
            }
        }
       
    
    • 内部也是一个无条件for (;;) 循环,只有在 Mssage 不为 null 的时候跳出循环,如果没有到执行时间就会一直循环。
    • 一直检测循环肯定会浪费CPU资源,那是如何解决的呢,答案就是 nativePollOnce 方法
      其中 nativePollOnce(ptr, nextPollTimeoutMillis) 是一个 native 方法,作用是释放CUP资原,阻塞线程 nextPollTimeoutMillis 毫秒;从代码可知,第一次循环的时候 nextPollTimeoutMillis 为0,是没有等待的,获取链表头部 mssage ,已知 MessageQueue 是根据执行时间排序的,所以头部最先执行。计算这个头部 Message 是否已经到了执行时间,如果已经到执行时间则直接返回。如果还没到执行时间则计算需要等待的时间
    • 如果在等待时间的时候,插入了新的立即执行 Message 怎么办?
      查看 MessageQueue.enqueueMessage() 内有一句 if (needWake) {nativeWake(mPtr);} ,在插入新 Message 时如果是同步 Message 会主动唤醒
    • Message 可分为三种
      1. barrier message : taget 为 null
      2. 同步 message :就是我们平常用的 message
      3. 异步 message : isAsynchronous = true,异步 message 配合 barrier 可获得优先执行权
      1
      2
      3
      4
      5
      6
      7
      
       if (msg != null && msg.target == null) {
                          // Stalled by a barrier.  Find the next asynchronous message in the queue.
                          do {
                              prevMsg = msg;
                              msg = msg.next;
                          } while (msg != null && !msg.isAsynchronous());
                      }
      

      这段代码的目的是:设置 Barrier 后优先执行异步 Message

  7. msg.target.dispatchMessage(msg); 调用 handler 的 dispatchMessage 处理 message
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
      public void dispatchMessage(@NonNull Message msg) {
         if (msg.callback != null) {
             handleCallback(msg);
         } else {
             if (mCallback != null) {
                 if (mCallback.handleMessage(msg)) {
                     return;
                 }
             }
             handleMessage(msg);
         }
     }
    
    • 优先执行 message.callback
    • 如果 message.callback 为 null,则执行 handler.mCallback
    • 如果 handler 的 mCallback 为 null 或者 返回 false 才执行 handler.handleMessage(message)

Handler 消息屏障机制(这是Handler内部机制并没有对外开放,主要用来提高 View 绘制消息的优先级)

当 通过方法 mHandler.getLooper().getQueue().postSyncBarrier() 向 MessageQueue 发送一个 target 为 null 的 Mssager ,就是添加一个屏障 当 MessageQueue 的第一个消息为屏障消息时,这个时候会只处理异步消息,直到屏障消息被移除

1
2
3
4
5
6
7
8
9
10
11
12
13
 @UnsupportedAppUsage
    Message MessageQueue.next() {
        //...
        if (msg != null && msg.target == null) {//如果是消息屏障
            //循环遍历消息,直到发现异步消息
            do {
                prevMsg = msg;
                msg = msg.next;
            } while (msg != null && !msg.isAsynchronous());
        }
        //...
    }

如果没有移除消息屏障,MessageQueue.next() 不会获取到同步消息

MessageQueue.addIdleHandler(@NonNull IdleHandler handler)

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
  Message next() {
    
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
           
                // 上面代码已经分析过了,如果当前没有要执行的 Message 才会执行下面方法 
                // 没有要执行的Message,可能是MessageQueue 为空,也能是延迟消息未到执行时机
            
                // 当没有消息要执行的时候,就会从 mIdleHandlers 取出回调并执行
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 当 mIdleHandlers 也为空的时候,变为阻塞状态
                    mBlocked = true;
                    continue;
                }
                // 从 mIdleHandlers 拷贝到 mPendingIdleHandlers
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
            // 遍历执行 mIdleHandlers
            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);
                    }
                }
            }

            pendingIdleHandlerCount = 0;
            
            nextPollTimeoutMillis = 0;
        }
    }

dispatchMessage

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

总结:一共有三个地方可以处理 message ,具体逻辑请看上面代码。

Messager 空闲阻塞实现

Looper

nativePollOnce nativeWake

Native Looper

epoll

epoll_create 创建一个 epoll 的句柄。 epoll_ctl 事件注册函数,可以监听文件的指定状态的变化,比如可读性等状态变化 epoll_wait 等待事件的产生,可以设置超时时间。