Android中異步消息處理機制

jopen 9年前發布 | 18K 次閱讀 Android Android開發 移動開發



1. Thread Local Storage (線程局部存儲)


     我們通過位于android.os包下的Looper.class源碼可以看到成員變量區有一個線程局部變量sThreadLocal,該類的作用是線程局部存儲?那么是線程局部存儲TLS?這個問題可以從變量作用域的角度來理解。

     

     變量的常見作用域一般包括以下幾種。

  • 函數內部變量。其作用區域是該函數,即每次調用該函數,該變量都會重新回到初始值。
  • 類內部的變量。其作用就是該類所產生的對象,即只要該對象沒有被銷毀,則對象內部的變量則一直保持。
  • 類內部的靜態變量。其作用是整個進程,即只要在該進程中,該變量的值就會一直保持,無論使用多少類來構造這個對象,該變量只有一個賦值,且一直保持。
  • </ul> 對于類內部的靜態變量而言,無論是從進程中那個線程引用該變量,其值總是相同的,因為編譯器內部為靜態變量分配了單獨的內存空間。但有時我們卻希望,當從同一個線程中引用該變量時,其值總是相同的, 而從不同的線程引用該變量,其值應該不同,即我們需要一種作用域為線程的變量定義,這就是線程局部存儲。 </div>


    ThreadLocal內部其實是使用弱引用WeakReference來存儲當前線程對象,但是不同的線程有不同的ThreadLocal對象,因此會有一個用于區別的id,由此內部封裝了一個靜態內部類Values,這個類就相當于一個map,存儲了每一個線程的實例的值。


    sThreadLocal在prepare()函數被調用的時候就進行了一次賦值,即構造一個新的Looper對象存儲到本地線程局部變量中,當然一個線程只能有一個Looper對象,否則會拋出如下圖所示的異常。


              


    Looper中有2個用于構造Looper對象的方法。一個是prepare(),另一個是prepareMainLooper()。 </div>

    prepare()我們已經說過了,下面說下prepareMainLooper()。

    prepareMainLooper(),由方法注釋我們可以知道,這個應用程序的mainlooper對象是由android環境創建的,所以你自己永遠沒必要調用這個函數。

                   


    2. Looper


    我們可以從類注釋來了解Looper類的作用到底是干什么的。 </div>

    Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare in the thread that is to run the loop, and then loop to have it process messages until the loop is stopped.

    Most interaction with a message loop is through the Handler class.

    This is a typical example of the implementation of a Looper thread, using the separation of prepare and loop to create an initial Handler to communicate with the Looper.

     class LooperThread extends Thread {
          public Handler mHandler;
    
          public void run() {
              Looper.prepare();
    
              mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                      // process incoming messages here
                  }
              };
    
              Looper.loop();
          }
      }
    Looper類的作用就是給在線程內部創建一個消息循環,當然線程自身內部是沒有一個消息循環機制;在run()函數首行調用 Looper.prepare(),即使創建了一個消息循環隊列,然后在run函數尾行調用Looper.loop()則開始處理消息直到消息循環停止。
    大多數消息循環的交互是通過Handler類進行的。


    3. Handler

    這里我們只從消息循環機制角度來分析這個Handler。首先看Handler的構造函數
    /**
         * Default constructor associates this handler with the queue for the
         * current thread.
         *
         * If there isn't one, this handler won't be able to receive messages.
         */
        public Handler() {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
             //從TLS(局部線程存儲)中取出已經存放好的Looper對象
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            //將Looper對象中的MessageQueue賦值給Handler中的對象
            mQueue = mLooper.mQueue;
            mCallback = null;
        }

    然后我們在從使用的角度分析,這里我只從異步處理UI界面分析。
    我們(程序員)通過 Handler調用sendMessage()函數在線程內部向UI主線程發送一個Message對象,該Message對象會依次通過Handler中的函數
    sendMessage(...) -> sendMessageDelayed(...) -> sendMessageAtTime(...) 
    最終會通過sendMessageAtTime發送消息對象。
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
        {
            boolean sent = false;
            MessageQueue queue = mQueue;
            if (queue != null) {
                msg.target = this;
           
                //將消息對象加入到消息隊列

                sent = queue.enqueueMessage(msg, uptimeMillis);
            }
            else {
                RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
            }
            return sent;
        }

    然后我們在來看看enqueueMessage進行了什么操作。

        final  boolean enqueueMessage(Message msg, long when) {
                          ...

            if (needWake) {
                nativeWake(mPtr);
            }

                          ...
       }

    nativeWake是一個java本地方法,這里涉及了消息機制中的Sleep-Wakeup機制,關于如何喚醒Looper線程的動作,這里不做贅述,其最終就是調用了 native層的Looper的wake函數,喚醒了這個函數之后,就開始進行消息循環
     
     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!