Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Handler機制

Android Handler機制

編輯:關於Android編程

Android Handler消息機制,在Android中扮演了很重要的角色。通過Android Handler,能夠實現延遲處理、子線程更新主線程界面等功能。(Android更新UI,一定需要在主線程中,這個規定的原因是,多個線程同時更新UI會造成UI界面更新混亂,不利於管理,因此,程序在初始化時,主線程中就初始化了UIHandler)。

Handler機制主要涉及到幾個類:
- Handler
- Looper
- ThreadLocal
- MessageQueue

後面將從簡單到復雜分析他們各自的功能,以及之間的關系。

ThreadLocal

ThreadLocal是一個線程相關的存儲類。ThreadLocal的存儲,獲取數據:

 public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
 public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

ThreadLocal中常用的方法主要是這兩個:
- set(T value)方法,傳入一個泛型對象,首先獲取當前線程的對象,然後values(currentThread),這個方法其實獲取currentThread.loacalValues,也就是說每個Thread都有localValues對象。如果這個values為空,則初始化,否則直接put添加這個數據。我們查看Values這個類的put方法:

  void put(ThreadLocal key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];

                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }

                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }

首先cleanup,這個方法會調用rehashed,如果數組容納的空間不夠,會進行空間擴展。然後外層是循環,循環的第一個判斷,如果找到原來設置的key,則直接修改原來key對應的值(保存的時候,是index保存鍵,index+1保存值);循環的第二個判斷,如果沒有找到對應的key,並且firstTombstone=-1,則在最後面保存key和value;第三個判斷,如果k!=null並且firstTombstone=-1和k==TOMBSTONE,那麼firstTomebstone=index,然後在下次循環中,將數據賦值到index這個位置(因為這個位置的數據是被刪除的位置,被刪除的數據會被賦值為TOMBSTONE)。


get() 這個方法中,通過hash計算出對應的index,並返回對應的值。 不做過多說明。

總結一下ThreadLocal:
1. 每個Thread都有一個Values對象,這個對象用來存儲數據。
1. ThreadLocal操作對應線程的數據,其實是操作在線程的Values中的數據。
1. put方法,會先檢查數組容量,然後將數據保存在數據中,如果其中有廢棄的數據,則會這個位置的數據會被覆蓋。
1. get方法,在線程的values中,查找指定位置的值。這個位置通過ThreadLocal的hash和Values的mask值來計算。
2. ThreadLocal是線程級保存數據的。

MessageQueue

它保存了由Looper分發的一系列事件Message,MessageQueue主要用來操作Message消息的,它核心的方法主要是:enqueueMessage,next,quite。

enqueueMessage
boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

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

            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
    }

分析一下上面方法的流程:
1. 首先判斷這個Message的Handler是否為空以及消息是否已經在使用。
1. 然後同步方法中,先判斷當前消息隊列是否退出,如果退出了則拋出異常,並將這個消息回收掉。
2. 如果消息隊列沒有退出,則開始將消息添加到隊列中:先將消息設置為正在使用中狀態(makeInUse),如果當前頭結點為空,則將這個消息Message作為頭結點,頭結點的名稱依然還是mMessages,這時,重新將阻塞的線程激活
3. 如果頭結點不為空,則先執行for循環,將p指向到隊列的末尾結點的next結點,最後將Message添加末尾結點的next結點,而Message的下一個結點指向p這個空結點。這樣就把新消息添加到了隊列末尾。
示意圖(將消息插入末尾):
這裡寫圖片描述


next<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjxiciAvPg0K1eK49re9t6i1xNf308PKx7TTTWVzc2FnZVF1ZXVl1tDIobP20rvM9c/7z6Kho8/Cw+bKx25leHS3vbeo1tC1xLK/t9a6y9DEtPrC6zoNCjxwcmUgY2xhc3M9"brush:java;"> ... for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } ... 外層是一個死循環,只有當這個方法獲取到一個Message時或者MessQueue調用了quite方法,才會跳出循環返回。 中間開始是一個同步的方法體,避免多線程同時訪問數據。 if先判斷是否存在無效的信息(target為空),如果target為空,開始循環找出後面的第一個異步消息。消息裡面有普通消息,同步消息,異步消息。同步消息的插入postSyncBarrier這個方法,同步消息沒有target,當代碼運行到這裡時,只會取出異步消息來執行。 如果msg不為空,先判斷現在的時間是否大於消息的時間,如果大於,則取出頭部的第一個消息(mMessage=msg.next)。這裡有可能因為preMsg!=null(第三步說的情況),因此 會執行prevMsg.next = msg.next。最終都是取出一條消息返回。
下面用圖來說明:
這裡寫圖片描述
quite
 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

主要是兩個判斷
1. 是否運行退出循環,主線程中的Looper是不允許的。
2. 是否安全退出,非安全退出,則隊列中所有的消息將會因為調用removeAllMessagesLocked()移除;安全退出只會刪除時間大於當前的消息,已經在隊列中可執行的消息不會刪除。

Looper

Looper的主要作用是在當前線程中運行一個消息循環隊列。sMainLooper是一個靜態變量,在主線程調用prepareMainLooper時候就會被賦值。因此這個的獲取就很簡單,調用getMainLooper就ok了。
後面主要講講Looper的核心方法,按照Looper的使用方法順序,prepare(),loop(),quite()方法說明。

prepare

public static void prepare() {
        prepare(true);
    }

    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靜態方法,會先new一個Looper對象,然後保存在當前線程中。


loop

  public static void loop() {
     ...

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);
    ...
        }
    }

上面是loop方法的核心代碼,主要是通過死循環不斷的調用queue.next()獲取隊列中的消息,並調用dispatchMessage將消息發送給Handler。消息隊列的next()方法當消息隊列中沒有消息後,會阻塞;當調用了quit方法後,next()返回null,然後loop的循環也會退出。總體來說,流程比較簡單。

Handler

Handler是發送消息和處理消息的主要參與者。下面主要分析發送消息和處理消息的相關方法:

enqueueMessage

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以發現,Handler裡面的sendMessage或者是post相關方法,最終都會調用enqueueMessage方法,最後其實是MessageQueue的enqueueMessage方法,這就回到上面MessageQUeue分析的過程了。


dispatchMessage

   public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

首先判斷Message有沒有設置callback,如果設置了直接調用callback處理,否則調用調用Handler的Callback或者是調用handleMessage方法處理。


以上就是Android中Handler的源碼分析。接下來我們通過常用的使用方式結合源碼再梳理一遍。

Looper.preprare()
Looper.looper
Handler handler = new Hander(looper)
hanlder.sendMessage
handleMessage

上面是整個用法的簡單寫法,Looper保存在prepare方法調用的所在線程中,Handle可以在其他線程中創建,只要能拿到looper對象,Handler的方法也可以在其他線程中調用。如果不指定looper對象,Handler綁定的就是當前線程的Looper對象。Looper對象的loop方法決定了Handler的handleMessage方法處理操作所在的線程。

這裡寫圖片描述
到此為止,Handler的機制基本就結束了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved