17 Handler-Java层

作者: 凤邪摩羯 | 来源:发表于2020-10-20 17:00 被阅读0次

    1 使用方式

    https://www.jianshu.com/p/e172a2d58905

    2 工作原理

    image.png
    image.png
    https://www.jianshu.com/p/f0b23ee5a922

    3 源码分析

    image.png

    https://www.jianshu.com/p/b4d745c7ff7a

    4 享元模式

    https://www.jianshu.com/p/ce6748cfe0d2

    https://www.jianshu.com/p/e252ddd16097

    5 Handler延时执行原理

    Handler的postDelay是通过设置Message的when为delay的时间,我们知道当我们的应用开启的时候,会同步开启Looper.loop()方法循环的,不停的通过MeassgeQueue的next方法:

    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;
        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;
          if (DEBUG) Log.v(TAG, "Returning message: " + msg);
          msg.markInUse();
          return msg;
         }
        } else {
         // No more messages.
         nextPollTimeoutMillis = -1;
        }
      ......
      }
     }
    

    当我们向MessageQueue插入一条延迟的Message的时候,Looper在执行loop方法,底层会调用epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);其中的timeoutMillis参数指定了在没有事件发生的时候epoll_wait调用阻塞的毫秒数(milliseconds)。这样我们在之前的时间内这个时候阻塞了是会释放cpu的资源,等到延迟的时间到了时候,再监控到事件发生。在这里可能有人会有疑问,一直阻塞,那我接下来的消息应该怎么执行呢?

    我们可以看到当我们插入消息的时候的方法:

    boolean enqueueMessage(Message msg, long when) {
      if (msg.target == null) {
       throw new IllegalArgumentException("Message must have a target.");
      }
      if (msg.isInUse()) {
       throw new IllegalStateException(msg + " This message is already in use.");
      }
      synchronized (this) {
       if (mQuitting) {
        IllegalStateException e = new IllegalStateException(
          msg.target + " sending message to a Handler on a dead thread");
        Log.w(TAG, e.getMessage(), e);
        msg.recycle();
        return false;
       }
       msg.markInUse();
       msg.when = when;
       Message p = mMessages;
       boolean needWake;
       if (p == null || when == 0 || when < p.when) {
        msg.next = p;
        mMessages = msg;
        needWake = mBlocked;
       } else {
        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;
       }
       mQuitting is false.
       if (needWake) {
        nativeWake(mPtr);
       }
      }
      return true;
     }
    

    阻塞了有两种方式唤醒,一种是超时了,一种是被主动唤醒了,在上面我们可以看到当有消息进入的时候,我们会唤醒继续执行,所以我们的即时消息在延迟消息之后插入是没有关系的。然后在延迟时间到了的时候,我们也会被唤醒,执行对应的消息send,以达到延迟时间执行某个任务的目的。
    优势:这种延迟在阻塞的时候,是会释放cpu的锁,不会过多地占用cpu的资源。

    6 补充

    image.png
    image.png
    image.png
    image.png

    7 常见问题

    https://www.jianshu.com/p/af52dd10058c

    相关文章

      网友评论

        本文标题:17 Handler-Java层

        本文链接:https://www.haomeiwen.com/subject/pgkjyktx.html