【Android源碼】Handler 機(jī)制源碼分析

為什么要使用Handler

  1. 因為在Android中訪問UI只能在主線程中進(jìn)行,如果在子線程中運行,則程序會拋出異常。

    // ViewRootImpl.java
    void checkThread() {
       if (mThread != Thread.currentThread()) {
           throw new CalledFromWrongThreadException(
                   "Only the original thread that created a view hierarchy can touch its views.");
       }
    }
    
  2. 為什么不允許在子線程中訪問UI?

    因為Android的UI并不是線程安全的,如果在多線程中執(zhí)行UI的操作,那么UI的狀態(tài)是不可控的,這個時候就會出現(xiàn)各種問題。
    那么最好的辦法就是只能在一個線程中執(zhí)行UI的操作。
    而Android主線程中又不能執(zhí)行耗時操作,因為那樣就會導(dǎo)致程序的ANR。
    所以Android提供了Handler這樣的機(jī)制,用來在子線程中執(zhí)行耗時操作之后,發(fā)送消息給主線程,主線程再執(zhí)行UI的更新操作。

Handler機(jī)制原理分析

這里我們以Android UI線程的Handler來分析。

Handler的創(chuàng)建

Android的應(yīng)用入口是ActivityThread.main()函數(shù),UI線程的Handler就是在這個函數(shù)中創(chuàng)建的。

public static void main(String[] args) {
    Looper.prepareMainLooper();
    thread.attach(false);
    ActivityThread thread = new ActivityThread();
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    Looper.loop();
}

當(dāng)main函數(shù)執(zhí)行之后,應(yīng)用就啟動了,從這個時候開始,Looper就會一直從消息隊列中取出消息并處理。而應(yīng)用會通過Handler來不斷的添加消息給消息隊列。通過不斷的添加消息和取出消息,整個消息機(jī)制就被運轉(zhuǎn)起來了。

Looper

從上面可以看到在Handler創(chuàng)建前后,通過Looper的prepareloop方法,創(chuàng)建了Looper對象和開啟消息循環(huán)。

  1. 構(gòu)造函數(shù)

    private Looper(boolean quitAllowed) {
       mQueue = new MessageQueue(quitAllowed);
       mThread = Thread.currentThread();
    }
    
    1. 創(chuàng)建了MessageQueue對象,這個對象是消息隊列,主要用來存放我們的消息,會在下面具體分析。
    2. 獲取當(dāng)前的線程并保存起來
  2. prepare

    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));
    }
    
    1. 判斷ThreadLocal中在當(dāng)前線程是否已經(jīng)存在了Looper對象,如果存在,則拋出異常,所以可以得出在同一個線程中只能存在一個Looper對象。
    2. 在ThreadLocal存入當(dāng)前線程的Looper對象。
    3. ThreadLocal對象是一個線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程存儲數(shù)據(jù),當(dāng)存儲數(shù)據(jù)之后,就只能在指定的線程中獲取到存儲的數(shù)據(jù),其他線程是不能獲取到數(shù)據(jù)的。
  3. loop

    public static void loop() {
       final Looper me = myLooper();
       // 獲取消息隊列
       final MessageQueue queue = me.mQueue;
        // 死循環(huán)來輪詢的從消息隊列中獲取消息
       for (;;) {
           Message msg = queue.next(); // might block
           if (msg == null) {
               return;
           }
           
           try {
               msg.target.dispatchMessage(msg);
           } finally {
               if (traceTag != 0) {
                   Trace.traceEnd(traceTag);
               }
           }
    
           msg.recycleUnchecked();
       }
    }
    
    1. loop函數(shù)會不斷的從消息隊列中取出消息,當(dāng)queue.next()為空的時候就直接阻塞住
    2. msg不為空,調(diào)用msg.target.dispatchMessage(msg)處理消息,而msg.target其實就是Handler對象,取出消息之后就交給Handler來處理發(fā)送的消息了。
  4. quit

    public void quit() {
       mQueue.quit(false);
    }
    public void quitSafely() {
        mQueue.quit(true);
    }
    

    調(diào)用MessageQueue的quit方法來退出Looper。其中上面的是直接退出,下面的是安全的退出,也就是只標(biāo)記一下,當(dāng)消息隊列中的消息全部執(zhí)行完成之后安全的退出。

    當(dāng)我們在子線程中創(chuàng)建Looper和Handler的時候,如果我們不調(diào)用quit方法的話,子線程就會一直處于等待狀態(tài),所以我們需要調(diào)用quit方法退出Looper。

MessageQueue

在Looper中大量的使用到了MessageQueue,而MessageQueue主要就是兩個操作插入消息和讀取消息并刪除消息,我們來一起分析下:

  1. 構(gòu)造函數(shù)

    MessageQueue(boolean quitAllowed) {
       mQuitAllowed = quitAllowed;
       mPtr = nativeInit();
    }
    

    在構(gòu)造函數(shù)中,調(diào)用了nativeInit方法在Native層創(chuàng)建了NativeMessageQueue和Looper,并將Looper設(shè)置給當(dāng)前的線程。這個時候Java層和Native層都有了MessageQueue和Looper。
    在Native層的Looper中,創(chuàng)建了一個管道,本質(zhì)上就是一個文件,一個管道中有讀和寫兩個文件操作符,通過讀和寫來將消息寫入和讀取。

  2. enqueueMessage 插入消息

    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 {
               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;
           }
    
           // We can assume mPtr != 0 because mQuitting is false.
           if (needWake) {
               nativeWake(mPtr);
           }
       }
       return true;
    }
    

    enqueueMessage其實就是構(gòu)建好msg,并插入到單鏈表中。

  3. next 讀取消息并刪除消息

    Message next() {
        for (;;) {
            nativePollOnce(ptr, nextPollTimeoutMillis);
        }
    }
    

    next方法是一個無限循環(huán)方法,當(dāng)消息隊列中沒有消息的時候,next方法會被阻塞在這里,當(dāng)有新的消息的時候,next方法會最終返回這條消息并從單鏈表中移除。

Handler

  1. 構(gòu)造函數(shù)

    public Handler(Callback callback, boolean async) {
    
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread that has not called Looper.prepare()");
       }
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
    }
    
    1. 首先獲取Looper對象,如果Looper對象為空,則拋出異常,所以當(dāng)我們在子線程中創(chuàng)建Handler的時候,首先要通過Looper.prepare()來創(chuàng)建子線程的Looper對象。
    2. 通過Looper獲取到MessageQueue
  2. 發(fā)送消息

    Handler的發(fā)送消息主要是post的方法和send的方法,而他們最終調(diào)用的都是:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
       MessageQueue queue = mQueue;
       if (queue == null) {
           RuntimeException e = new RuntimeException(
                   this + " sendMessageAtTime() called with no mQueue");
           Log.w("Looper", e.getMessage(), e);
           return false;
       }
       return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    Handler主要就是向消息隊列中插入了一條消息,這個時候MessageQueue就會調(diào)用插入的方法來插入消息。

  3. 讀取消息

    當(dāng)Handler發(fā)送消息之后,MessageQueue就會通過next方法讀取出這個消息,那么Looper的loop就不會被阻塞住,取出消息之后就調(diào)用了msg.target.dispatchMessage(msg)方法,最終交給Handler的dispatchMessage來執(zhí)行。

    /**
    * 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);
       }
    }
    
    1. 檢查Message的callback是否為空,不為空則通過handleCallback來處理,而callback就是通過post傳遞過來的Runnable對象。直接調(diào)用run方法執(zhí)行。

      private static void handleCallback(Message message) {
          message.callback.run();
      }
      
    2. 檢查mCallback是否為空,不為空則調(diào)用mCallback.handleMessage方法處理消息。而mCallback就是我們在創(chuàng)建Handler的時候傳遞過去的callback。

      public interface Callback {
          public boolean handleMessage(Message msg);
      }
      public Handler(Callback callback) {
          this(callback, false);
      }
      
    3. 調(diào)用handleMessage(msg)處理消息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容