android的消息機(jī)制

handler學(xué)習(xí)思路

  1. handler是是什么,做什么用,相關(guān)知識了解?
  2. handler主線程代碼示例
  3. handler子線程代碼示例
  4. handler,MessageQueue,Looper,ThreadLocal,的關(guān)系及源碼解析

一、Handler是啥?用來做什么的? 為什么要這么用?

1.在了解handler是啥之前,先通俗的講一下android通信的一些題外內(nèi)容。

  • 進(jìn)程: 是系統(tǒng)進(jìn)行資源分配和調(diào)度的最小單位,是一個具有一定功能的程序的對數(shù)據(jù)進(jìn)行操作的活動比如一個程序(APP)是至少在一個進(jìn)程運行的,也可以通過給相關(guān)組件設(shè)置Process參數(shù)來設(shè)置在其其他進(jìn)程運行。

  • 線程: 是CPU調(diào)度和分派的基本單位,通俗的講就是電腦手機(jī)可以獨立運行的最小單位,線程沒有自己獨立的內(nèi)存,而是在進(jìn)程里面的,同一進(jìn)程里面的線程共享自己所屬進(jìn)程的所有資源

  • 系統(tǒng)通信: 通俗的講就是數(shù)據(jù)或者信號在系統(tǒng)內(nèi)部或者系統(tǒng)間傳遞的過程,又因為系統(tǒng)里面有進(jìn)程和程的單元,所以就有了跨進(jìn)程通信 IPC機(jī)制(Socket、AIDL、Message、Binder、ContentProvider、以及IO文件系統(tǒng))和跨線程通信 Handler機(jī)制(Handler)。

  • 線程同步: 只一個對象在同一時間只能被操作處理一次,而不允許被并發(fā)操作,典型的有兩類方式。

  • 一、同步鎖機(jī)制(synchronized):及上過鎖的對象會被處理的時候阻塞在消息隊列中等待前面一個對象執(zhí)行完畢再跟著執(zhí)行(以時間的方式換取同步操作)。

  • 二、Handler 來處理UI線程的操作: 通過子線程操作數(shù)據(jù),然后mUIHandler將消息發(fā)送到主線程的MessageQueue中,等待looper分發(fā)給對應(yīng)的mUIHandler來處理消息,(用空間方式換取同步操作,ps:因為耗時操作被并發(fā)執(zhí)行,而UI更新的時間很小)。

2.Handler通常干嘛用的? 為嘛要用Handler?

  • Handler是用來做跨線程通信只用,通常是用來解決子線程數(shù)據(jù)處理,UI線程更新之用

  • 原因之一: UI更新限制:android默認(rèn)更新UI操作的時候有一個在ViewRootImpl中的方法checkThread來驗證是否是UI線程;

 // 驗證是否在UI線程執(zhí)行UI操作
 void checkThread(){
     if (mThread != Thread.CurrentThread()) {
     throw new CalledFromWrongThreadException("Only the original thread that created a view
               hierarchy can touch its views");
     }
 }

但是又因為android規(guī)定:UI線程如果5秒無法響應(yīng)屏幕點擊事件或者廣播接收事件,則會報系統(tǒng)未響應(yīng)(ANR),故:耗時的數(shù)據(jù)操作就在子線程進(jìn)行,再通過Handler回傳給UI線程進(jìn)行UI更新操作,所以Handler就通常用來做UI更新操作,(ps:系統(tǒng)廣播是10未響應(yīng)也會出現(xiàn)ANR

  • 原因之二: UI更新同步問題: android的控件如果在多線程并發(fā)中執(zhí)行操作,會導(dǎo)致控件出現(xiàn)不可控制的狀態(tài),所以需要控件同步操作;同步分兩種,時間操控的同步鎖,控件操控的多線程并發(fā)Handler操作;之所以不用同步鎖,1.同步鎖會大大增加UI控件的邏輯;2.其次同步鎖會阻礙其他線程對這個控件的操作,影響UI執(zhí)行效率,導(dǎo)致其他線程阻塞;所以Handler就被用來操作單線程的UI更新操作;

二、Handler在UI線程中代碼示例

  • 示例一:(new handler的衍生類來實現(xiàn)handMessage(Message msg))
      Handler mHander = new Handler(){
        @Override
        public void handMessage(Message msg){
            // 執(zhí)行消息處理
            switch(msg.what){
              case 0:
                 log.d(“處理消息1的消息”);
              break;
            }
        }
      }

       new Thread("Thread 2") {
            @Override
            public void run() {
                super.run();
                Log.d(TAG, "run: [Thread 2 的uid 為] : " + currentThread().getId() + " 值為 " +mThreadLocal.get());
                mHandler.sendEmptyMessage(0);
                Log.d(TAG, "run: 當(dāng)前時間 " + System.currentTimeMillis());
            }
        }.start();
  • 示例二:(new Handler.CallBack() 不使用Handler的衍生來生成新的Handler)
      // 創(chuàng)建Handler的回調(diào)接口
      private Handler.Callback mHandlerCallBack = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.d(TAG, "handleMessage: 接收到的消息內(nèi)容為: " + msg.obj.toString());
                    return true;
            }
            return false;
        }
      };

        private Handler mCallBackHandler = new Handler(mHandlerCallBack);

        new Thread("Thread 1") {
            @Override
            public void run() {
                super.run()
                String msgs = "[Thread 1 線程uid ] : " + currentThread().getId();
                Message message = new Message();
                message.what = 1;
                message.obj = msgs;
                mCallBackHandler.sendMessage(message);
            }
        }.start();
    

三、Handler在非UI線程的實現(xiàn)

     private Handler mThreadHadnler;

      new Thread("handlerThread") {
            @Override
            public void run() {
                Looper.prepare();
                mThreadHadnler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                            case 3:
                                Log.d(TAG, "handleMessage: 獲取到的消息內(nèi)容為:" + msg.obj.toString());
                                break;
                        }
                    }
                };
                Looper.loop();
            }
        }.start();

         new Thread("sonThread"){
            @Override
            public void run() {
                String msgs = "[sonThread 線程uid ] : " + currentThread().getId() ;
                Message message = new Message();
                message.what = 3;
                message.obj = msgs;
                mThreadHadnler.sendMessage(message);
            }
        }.start();

四、Handler的源碼分析

  • 1.Handler工作流程圖
    handler工作流程圖.png
  • 2.ThreadLocal,Message, MessageQueue,Hadnler解釋

1.ThreadLocal解析 : 是一個可以用來存儲線程獨有的數(shù)據(jù)的類, 作用是獲取線程loop取消息(參見方法四和五);

ThradLocal的原理.png

 /**   方法一
    * Sets the current thread's copy of this thread-local variable
    * to the specified value.  Most subclasses will have no need to
    * override this method, relying solely on the {@link #initialValue}
    * method to set the values of thread-locals.
    *
    * @param value the value to be stored in the current thread's copy of
    *        this thread-local.
    */
   public void set(T value) {
       Thread t = Thread.currentThread();
       ThreadLocalMap map = getMap(t);   // 拿到線程的ThreadLocalMap對象進(jìn)行存儲
       if (map != null)
           map.set(this, value);   
       else
           createMap(t, value);
   }

 /**   方法二
    * Returns the value in the current thread's copy of this
    * thread-local variable.  If the variable has no value for the
    * current thread, it is first initialized to the value returned
    * by an invocation of the {@link #initialValue} method.
    *
    * @return the current thread's value of this thread-local
    */
   public T get() {
       Thread t = Thread.currentThread();
       ThreadLocalMap map = getMap(t);   // 拿到線程的ThreadLocalMap對象進(jìn)行存儲
       if (map != null) {
           ThreadLocalMap.Entry e = map.getEntry(this);
           if (e != null) {
               @SuppressWarnings("unchecked")
               T result = (T)e.value;
               return result;
           }
       }
       return setInitialValue();
   }

  /**   方法三
    * Get the map associated with a ThreadLocal. Overridden in
    * InheritableThreadLocal.
    *
    * @param  t the current thread
    * @return the map
    */
   ThreadLocalMap getMap(Thread t) {
       return t.threadLocals;
   }


  /**    方法四
    * Run the message queue in this thread. Be sure to call
    * {@link #quit()} to end the loop.
    */
   public static void loop() {
       final Looper me = myLooper();
   }

 /**    方法五
    * Return the Looper object associated with the current thread.  Returns
    * null if the calling thread is not associated with a Looper.
    */
   public static @Nullable Looper myLooper() {
       return sThreadLocal.get();
   }

2.Message的解析: 跨線程通信的信息的載體, 底層運用了IPC跨進(jìn)程通信的知識(目前沒有解析>native層源碼,故如有問題歡迎指正,待完善native的源碼再來完善),有兩種創(chuàng)建方式:1.new 對象; 2.通過消息池,及obtain()系列

3.MessageQueue的解析: 跨線程通信的消息列表的通道,底層運用了管道的內(nèi)容(目前沒有解析>native層源碼,故如有問題歡迎指正,待完善native的源碼再來完善)

  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 {
                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);
           }
   }

4.Looper的解析: Looper及消息通道的消息分配人員,通過死循環(huán)來實現(xiàn)讀取MessageQueuenext出來的消息,如果消息為null,及跳出了looper的循環(huán),所以有兩種方法跳出looper,quit()方法一和quitSafely()方法二跳出之后后續(xù)的Message無法交給Handler處理;loop()方法三通過MessageQueue獲取Message,然后傳遞給Message.disPatchMessage(msg)處理;

/**      方法一
   * Quits the looper.
   * <p>
   * Causes the {@link #loop} method to terminate without processing any
   * more messages in the message queue.
   * </p><p>
   * Any attempt to post messages to the queue after the looper is asked to quit will fail.
   * For example, the {@link Handler#sendMessage(Message)} method will return false.
   * </p><p class="note">
   * Using this method may be unsafe because some messages may not be delivered
   * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
   * that all pending work is completed in an orderly manner.
   * </p>
   *
   * @see #quitSafely
   */
  public void quit() {
      mQueue.quit(false);
  }

 /**    方法二
   * Quits the looper safely.
   * <p>
   * Causes the {@link #loop} method to terminate as soon as all remaining messages
   * in the message queue that are already due to be delivered have been handled.
   * However pending delayed messages with due times in the future will not be
   * delivered before the loop terminates.
   * </p><p>
   * Any attempt to post messages to the queue after the looper is asked to quit will fail.
   * For example, the {@link Handler#sendMessage(Message)} method will return false.
   * </p>
   */
  public void quitSafely() {
      mQueue.quit(true);
  }

   /**   方法三
   * Run the message queue in this thread. Be sure to call
   * {@link #quit()} to end the loop.
   */
  public static void loop() {
      final Looper me = myLooper();
      if (me == null) {
          throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
      }

      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;
          }          
          try {
              msg.target.dispatchMessage(msg);    // 此處的Handler及發(fā)送Message的Handler
              end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
          } finally {
              if (traceTag != 0) {
                  Trace.traceEnd(traceTag);
              }
            }
       }
  }

5.Handler的解析: 發(fā)送消息到隊列以及處理Looper分配的消息。發(fā)送消息兩種: >sendMessage(Message msg)系列 方法一 和post(Runnable r)系列 方法二;自身兩種處理消息方式:>handleMessage(msg)方法三 以及CallBack接口中的handleMessage(msg)系列 方法四;

  /**     方法一
    * Pushes a message onto the end of the message queue after all pending messages
    * before the current time. It will be received in {@link #handleMessage},
    * in the thread attached to this handler.
    *  
    * @return Returns true if the message was successfully placed in to the 
    *         message queue.  Returns false on failure, usually because the
    *         looper processing the message queue is exiting.
    */
   public final boolean sendMessage(Message msg)
   {
       return sendMessageDelayed(msg, 0);
   }

 /**   方法二
    * Causes the Runnable r to be added to the message queue.
    * The runnable will be run on the thread to which this handler is 
    * attached. 
    *  
    * @param r The Runnable that will be executed.
    * 
    * @return Returns true if the Runnable was successfully placed in to the 
    *         message queue.  Returns false on failure, usually because the
    *         looper processing the message queue is exiting.
    */
   public final boolean post(Runnable r)
   {
      return  sendMessageDelayed(getPostMessage(r), 0);
   }

   /**  方法三
    * Handle system messages here.
    */
   public void dispatchMessage(Message msg) {
       if (msg.callback != null) {
           handleCallback(msg);
       } else {
           if (mCallback != null) {
               if (mCallback.handleMessage(msg)) {
                   return;
               }
           }
           handleMessage(msg);
       }
   }

 /**   方法四
    * Callback interface you can use when instantiating a Handler to avoid
    * having to implement your own subclass of Handler.
    *
    * @param msg A {@link android.os.Message Message} object
    * @return True if no further handling is desired
    */
   public interface Callback {
       public boolean handleMessage(Message msg);
   }

五、Handler機(jī)制的注意以及疑問和后續(xù)的學(xué)習(xí)內(nèi)容

handler機(jī)制中的關(guān)系

  • 一個APP至少一個進(jìn)程,
  • 一個進(jìn)程至少一個線程,
  • 一個線程僅有一個looper(初始線程沒有l(wèi)ooper,需要自己prepare()),一個MessageQueue,可以有多個handler;
  • 一個線程如果需要通過Handler發(fā)送一個消息,必須初始化一個looper
  • 主線程main方法已經(jīng)自動幫我們初始化了looper 可以通過getMainLooper()獲取,并且主線程的Looper不可以被手動quit()掉;
  • 消息是被handler發(fā)送到所在線程的MessageQueue中;

hander機(jī)制的疑問

  • Message為什么可以從子線程被Handler發(fā)送到主線程?
  • MessageQueue是如何實現(xiàn)消息的存儲以及分發(fā)的?
  • MessageQueue會不會被放滿?
  • MessageQueue既然一有消息就可以分配,那沒消息它在干嘛?如果是停著啥也不干那又是如何做到阻塞的?為什么阻塞不會造成ANR呢?
  • 線程又是如何共享進(jìn)程中所有資源的?
  • 線程可以被開啟多少個?頻繁開啟消耗資源,那有沒有什么好的方法可以不那么消耗資源的使用線程?
  • 線程,進(jìn)程,和堆,棧有什么關(guān)系么?
  • 跨進(jìn)程通信又是啥?

學(xué)習(xí)是一個尋尋漸進(jìn)的過程,我們既要學(xué)會觀察現(xiàn)象,了解現(xiàn)象,也需要透過現(xiàn)象看本質(zhì)。最后,祝愿自己及廣大開發(fā)猿能夠在技術(shù)的道路上越走越遠(yuǎn)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容