Handler机制的结构

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();
          }
        }
        
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容