美文网首页
Handler机制的结构

Handler机制的结构

作者: 吕注意 | 来源:发表于2020-11-20 22:39 被阅读0次

    Handler消息机制由四部分组成:

    • Looper:
    • MessageQueue:
    • Handler:
    • Message

    Looper、MessageQueue、Handler、Message 之间的关系
    从设计模式角度来看,Handler机制可以说是一个生产者-消费者模式:
    Handler 是生产者,生产 Message,并将 Message放入 MessageQueue队列中。而 Looper 是消费者,它从 MessageQueue队列中拿到Message进行处理(消费)。

    结构图.png

    Handler消息机制的实现

    • Message类:消息的封装

      成员变量 说明
      target 调用enqueueMessage()方法的Handler对象实例
      next MessageQueue中下一个Message对象
      when 处理时期,在MessageQueue中Message对象按升序排列
      ...
      • Message的复用:

        因为Android中消息是通过Handler机制驱动的,很多消息都需要用到Handler机制。如果每次消费完一个Message就将对象设置为null,让出内存,在生产Message时又得使用new去重新申请内存,可能会造成内存抖动问题。所以在消费完Message后,不会让出Message内存,而是将其内容置空,留下一个Message壳,在生产下一个Message时可以直接复用该Message。

        1. Message.recycleUnchecked()方法中,会将Message对象的所有配置都重置,只留下一个Message空壳。

          //消费完Message后,重置内容。
          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. 在重置完Message对象后,使用obtain()方法可以拿到被重置完的Message对象,并进行复用

          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();
          }
          ​
          ​
          //Activity中
          new Handler().sendMessage(Message.obtain());
          
          
    • MessageQueue类:传送带,负责存放Message消息,存放消息方法是enqueueMessage()

      成员变量 说明
      mQuitAllowed:boolean 如果消息队列可以退出,则为真。主线程中为fasle
      mMessages:Message 列表中第一个Message消息。后面message由next获得
      mQuitting:boolean 是否正在退出标记位
      ...

      enqueueMessage()中主要就是将要加入的message根据时间升序加入链表中,然后在需要的时候调用nativeWake()去唤醒Looper。插入链表是按照when升序进行排序的。

       //MessageQueue.java
      boolean enqueueMessage(Message msg, long when) {
      //省略
      
        synchronized (this) {
          //如果MessageQueue正在退出
            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;
            }// end 如果MessageQueue正在退出
      
            msg.markInUse();
            msg.when = when;
            //MessageQueue中保存着第一个Message,即mMessages
            Message p = mMessages;
            //是否需要唤醒标记位
            boolean needWake;
            //按照时间排序,与msg对比,when小的排前面
            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;
            }
      
            if (needWake) {
                nativeWake(mPtr);
            }
          }
          return true;
      }
      
    • Handler类:生产(发送)消息的源头

      在Handler类中,无论我们调用sendMessage()、postMessage()还是其他的方法,最终都会调用到enqueueMessage()方法上。

      enqueueMessage()方法最终走到了MessageQueue.enqueueMessage()

      Handler handler = new Handler();
      handler.sendMessage(Message.obtain());
      
      
      //Handler.java 
      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);
      }
      
    • Looper类:处理消息,主要通过loop()方法中的死循环获取MessageQueue中的Message

      主线程的Looper在应用启动时便启动且开始了死循环,它是在ActivityThread.main()方法中初始化并启动的。Looper的初始化调用的是Looper.prepareMainLooper()

      //ActivityThread.java
      public static void main(String[] args) {
          //省略
      
          Looper.prepareMainLooper();//<<<<看这里
      
          //省略
      
      }
      
      1. Looper.prepareMainLooper()方法中,调用preapre(false)对Looper进行初始化。其中参数false最终会被设置到MessageQueue中的成员变量mQuitAllowed中,表示该线程对应的MessageQueue不能调用quit()方法。

        //Looper.java
        public static void prepareMainLooper() {
          prepare(false);
          synchronized (Looper.class) {
              if (sMainLooper != null) {
                  throw new IllegalStateException("The main Looper has already been prepared.");
              }
          sMainLooper = myLooper();
          }
        }
        
        
        void quit(boolean safe) {
          //如果 mQuitAllowed == fasle ,即不允许退出。则调用该方法会抛出异常
          if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
          }
        
          //省略
        }
        
      2. prepare()中,会去判断当前线程是否已经存在一个Looper对象实例,如果没有则通过new Looper()新建一个Looper对象实例并保存到sThreadLocal中。如果该线程原本就有了一个Looper对象实例,则抛出异常。因此一个线程只能有一个Looper对象实例

        //Looper.java
        private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
        

        如何保证每个线程中Looper的唯一?

        答:通过在prepare()方法中对当前线程Looper对象实例是否唯一进行检测,且新建好的Looper会存储在静态常量sThreadLocal中,两部分来保证线程中Looper实例的唯一。

      3. new Looper()中,会一并初始化MessageQueue对象实例。也就是说每个Looper会持有一个MessageQueue对象,且因为Looper在线程中的唯一性,MessageQueue在线程中也是唯一的。

        private Looper(boolean quitAllowed) {
          mQueue = new MessageQueue(quitAllowed);
          mThread = Thread.currentThread();
        }
        
      4. 初始化完Looper后,调用loop()方法进行死循环获取MessageQueue中的message进行操作

        //ActivityThread.java
        public static void main(String[] args) {
        //省略
        
            Looper.prepareMainLooper();
        
        //省略
        
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
        
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
        
        //省略
        
            Looper.loop();//<<<<<<<<<<<<<<<<<看这里
        
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
        
      5. loop()

        1. 拿到当前线程中的MessageQueue,并且调用了MessageQueue.next(),在next()中继续用死循环获取Message,如果下一个Message处理的时机还没到,会进行阻塞休眠。

          Message next() {
          
            //mPtr 类似线程的id的东西,在休眠和唤醒时有用。在native层初始化
            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();
            }
          
          //使用native的方法休眠,时长 == nextPollTimeoutMillis
              nativePollOnce(ptr, nextPollTimeoutMillis);
          
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
             
                //省略
              
              if (msg != null) {
                  //通过when判断下一个Message到时间处理没
                  if (now < msg.when) {
                  //下一个Message还没有准备好。设置一个超时以在它准备好时唤醒。
                      nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                  } else {
                  //下一个Message到时间处理了
                      mBlocked = false;
                      if (prevMsg != null) {//这个跟异步Message有关
                          prevMsg.next = msg.next;
                      } else {//一般的同步Message
                          //将第二个Message设置给mMessages,
                          mMessages = msg.next;
                      }
                      msg.next = null;
                      if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                      msg.markInUse();
                      return msg;//<<<<<<<<<< 正常情况下返回
                  }
              } else {
                  // 没有消息,设置 -1 ,将无限期休眠。
                  //在下一个循环中调用 nativePollOnce()时休眠
                  nextPollTimeoutMillis = -1;
              }
              
              //调用quit()时,mQuitting==true。
              //主线程不能调用
              if (mQuitting) {
                  dispose();
                  return null;//<<<<<<<<<<< 子线程调用quit()后,会返回null
              }
          
              //省略
            }
           }
          
        2. 拿到要处理的Message后,判空,如果为空,则退出Loop循环。非空则调用handler中的dispatchMessage()方法进行分发,实现线程间通信。

           public static void loop() {
            final Looper me = myLooper();
          
            final MessageQueue queue = me.mQueue;
          
            //省略
          
            for (;;) {
                Message msg = queue.next(); // might block
          
                //判空,结束loop死循环
                if (msg == null) {
                    return;
                }
          
          
                //省略
          
                try {
                    //msg.target == handler
                    msg.target.dispatchMessage(msg);
                    if (observer != null) {
                        observer.messageDispatched(token, msg);
                    }
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } catch (Exception exception) {
                    if (observer != null) {
                        observer.dispatchingThrewException(token, msg, exception);
                    }
                    throw exception;
                } finally {
                    ThreadLocalWorkSource.restore(origWorkSource);
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                  }
             }
          
          
              //省略
          
              //Message清空数据。复用机制
              msg.recycleUnchecked();
            }
          }
          

    相关文章

      网友评论

          本文标题:Handler机制的结构

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