Android異步消息機制Handler詳解,源碼剖析(API 23)

聲明:Android不同API版本中同一個類的實現方法可能會有不同,本文是基於最新的API 23的源碼進行講解的。


Android的UI是線程不安全的,也就是說在子線程中操作主線程(UI線程)程序就會崩潰。想在子線程中更新UI就需要用到異步操作機制。

Android的異步操作主要有兩種,AsyncTask和Handler。AsyncTask的詳細講解和源碼分析在我的另一篇博客中進行了詳細的講解,本篇就來講解一下Handler的用法與源碼分析。


基本用法


在Activity中有一個Button和一個EditText,當點擊Button之後,每1秒鐘更新一下這個EditText中的文字。

按鈕的Click方法
handlerBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Timer timer = new Timer();
                timer.scheduleAtFixedRate(new MyTask(), 1, 1000);
            }
        });

private class MyTask extends TimerTask {
        @Override
        public void run() {

            Message message = new Message();
            message.what = 1;
            mHandler.sendMessage(message);
        }
    }

Handler處理方法

 private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            EditText editText = (EditText)findViewById(R.id.editText_handler);
            editText.setText("Handler Test " + i);
            i++;
        }
    };

效果



源碼分析


Handler機制流程圖

1. Message 
Message 消息,理解爲線程間交流的信息,處理數據後臺線程需要更新 UI ,則發送Message 內含一些數據給 UI 線程。 
2. Handler 
Handler處理者,是 Message 的主要處理者,負責 Message 的發送,Message 內容的執行處理。後臺線程就是通過傳進來的 Handler對象引用來 sendMessage(Message)。 而使用 Handler,需要 implement 該類的 handleMessage(Message)方法,它是處理這些 Message 的操作內容,例如 Update UI 。通常需要子類化 Handler 來實現 handleMessage方法。 
3. Message Queue 
Message Queue 消息隊列,用來存放通過 Handler 發佈的消息,按照先進先出執行。每個 message queue 都會有一個對應的 Handler。Handler 會向 messagequeue 通過兩種方法發送消息:sendMessage 或 post。這兩種消息都會插在 message queue 隊尾並按先進先出執行。但通過這兩種方法發送的消息執行的方式略有不同:通過 sendMessage發送的是一個 message 對象,會被 Handler 的 handleMessage()函數處理;而通過 post 方法發送的是一個 runnable 對象,則會自己執行。 
4. Looper 
Looper 是每條線程裏的 Message Queue 的管家。Android 沒有 Global 的MessageQueue,而 Android 會自動替主線程(UI 線程)建立 Message Queue,但在子線程裏並沒有建立 Message Queue。 所以調用 Looper.getMainLooper()得到的主線程的 Looper 不爲 NULL,但調用 Looper.myLooper()得到當前線程的 Looper 就有可能爲 NULL。


Handler構造函數

 public Handler(Callback callback, boolean async) {
        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());
            }
        }

        <strong>mLooper = Looper.myLooper();</strong>
        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;
    }

Looper.myLooper()這個方法之後對mLooper進行了判斷,如果爲空就報錯"Can't create handler inside thread that has not called Looper.prepare()",接下來就看看這個方法裏到底有什麼。

    /**
     * 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();
    }

原來就是返回一個當前線程的Looper啊,沒有的話就返回null。這也就解釋了爲什麼在子線程中必須先調用Looper.prepare()了。在主線程main()方法中已經調用了Looper.prepare(),所以在主線程中可以直接new一個Handler而不用事先調用Looper.prepare()。

說了那麼多的Looper.prepare(),下面來看一下到底什麼是Looper.prepare()。

 /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }
先判斷一下sThreadLocal中有沒有Looper,沒有就創建一個。同時也可以發現一個線程中只能有一個Looper。

Message


我們使用handler的時候需要先sendMessage()再handleMessage(),現在就來分析一下這個Message到底是怎麼發送出去又是怎麼接到的。

<span style="font-size:18px;"> public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }</span>

<span style="font-size:18px;"> public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }</span>

<span style="font-size:18px;">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);
    }</span>
可以看到跳來跳去最後跳到了sendMessageAtTime(),在這個方法中對MessageQueue進行了判斷並在最後調用了enqueueMessage()。

<span style="font-size:18px;">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;
    }</span>

用了一個mMessages對象表示當前待處理的消息。消息入隊就把所有的消息按照時間的順序排列,根據時間的順序調用msg.next來指定它的下一個消息。如果你用sendMessageAtFrontOfQueue()發送消息,系統也會調用enqueueMessage(),只不過時間爲0,並且把mMessages賦值爲新入隊的這條消息,然後將這條消息的next指定爲剛纔的mMessages,完成了添加消息到隊列頭部的操作。

消息出隊
Looper.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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

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

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }
這裏有一個死循環,queue.next()就是出隊的方法。
Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        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();
            }

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

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

如果當前MessageQueue中存在mMessages(即待處理消息),就將這個消息出隊,每當有一個message出隊就調用msg.target.dispatchMessage(msg) (msg.target是一個Handler)然後讓下一條消息成爲mMessages,否則就進入一個阻塞狀態,一直等到有新的消息入隊。

Handler.dispatchMessage()
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
如果mCallback不爲空,則調用mCallback的handleMessage()方法,否則直接調用Handler的handleMessage()方法,並將消息對象作爲參數傳遞過去。

到此爲止整個Handler的消息傳遞過程就已經全部講完了,相信大家一定對Handler有了更深入的理解。

一個標準的Handler

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





發佈了58 篇原創文章 · 獲贊 26 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章