從源碼分析Handler機制,源碼handler機制

來源:互聯網
上載者:User

從源碼分析Handler機制,源碼handler機制

在Android中,當要更新ui的時候,我們必須要在主線程中進行更新,原因時當主線程被阻塞了5s以上就會出現anr異常,會導致程式崩潰。所以一些耗時的操作必須要放在子線程中,但是在子線程中又不能做更新ui的操作,所以為瞭解決這個問題,Android設計了handler機制,handler的出現建立起了主線程與子進程之間的通訊橋樑,使得ui更新問題得到改善,下面就來剖析一下handler。ActivityThread啟動了應用程式的主線程,在ActivityThread的main方法中:

public static final void main(String[] args) {        SamplingProfilerIntegration.start();        Process.setArgV0("<pre-initialized>");        Looper.prepareMainLooper();        if (sMainThreadHandler == null) {            sMainThreadHandler = new Handler();        }        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();       ......}

從上述代碼可以看出,首先要執行Looper.prepareMainLooper();操作,然後進入loop進行迴圈。在prepareMainLooper中,調用prepare方法使用sThreadLocal給當前線程設定一個Looper,如果當前線程中沒有,就初始化一個Looper,在Looper的構造方法中順便建立了一個MessageQueue。細心的讀者可能會注意到prepareMainLooper和prepare方法都是static的,sThreadLocal也是個靜態變數,首先不考慮子線程存在的情況,只考慮主線程,所以無論我們在應用程式的哪個地方調用Looper.prepareMainLooper();通過sThreadLocal.get()得到的都是同一個looper對象,這樣就可以保證一個線程中只有一個Looper對象,那麼也就意味著一個線程中只有一個MessageQueue。

public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        } }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)); }private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();}

執行完 Looper.prepareMainLooper()之後,就是開始Looper.loop()進行訊息的迴圈讀取並且進行分發,這個稍後分析完Handler後再分析。

下面我們再分析一下Handler。
在代碼中我們經常的這樣用:

private Handler handler = new Handler(){    public void handleMessage(Message msg) {        // process incoming messages here    }}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;}

在Handler的構造方法中,通過Looper.myLooper()擷取本線程中唯一的一個Looper對象,並且初始化hanlder中的訊息佇列,這個訊息佇列和Looper中的一開始初始化的訊息佇列是同一個。
當調用handler.sendMessage或者sendEmpty方法時,最終要走的方法都是sendMessageAtTime方法:

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

在enqueueMessage方法中,給當前要加入訊息佇列的msg設定一個target為this,這個this也就是當前的handler對象,主要是為了後面的looper迴圈出訊息後,方便知道這個msg向何處分發,該由哪個handler進行處理。接著就調用MessageQueue的enqueueMessage方法將msg排入佇列中。

boolean enqueueMessage(Message msg, long when) {    ......    synchronized (this) {        ......        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;        }        if (needWake) {            nativeWake(mPtr);        }    }    return true;}

當訊息對列中沒有任何msg的時候,當前加入的msg就應該是隊列的隊頭,並且從else語句我們可以看出,整個訊息對列是個迴圈對列。此時訊息對列中已經有了msg,那麼這個msg應該被接受並進行分發處理,在ActivityThread中調用了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) {            return;        }        Printer logging = me.mLogging;        .......        msg.target.dispatchMessage(msg);        final long newIdent = Binder.clearCallingIdentity();        if (ident != newIdent) {        }        msg.recycle();    }}

其中的for迴圈為死迴圈,有人可能納悶了,looper.loop()是運行在主線程中的,而其中有是個死迴圈,不是說好的主線程中不能做逾時的操作嗎?呵呵,因為在迴圈中輪詢訊息佇列中的訊息時候,如果沒有訊息,則會被阻塞。所以這裡不用擔心anr的問題。通過queue.next()擷取出msg後,通過msg.target.dispatchMessage(msg)處理這個訊息,這個msg.target就是要處理訊息的handle,這也就是為啥在handler中要重寫dispatchMessage方法的原因。最後調用recycle釋放訊息,之所以要recycle一下,是因為Message可以不用new的方式,也可以通過Message.obtain方法從訊息池中擷取一個,因為訊息池中的訊息個數有限,如果用完訊息後,不及時的recycle的 話,就會造成msg對象不能重複利用。

接下來具體的分析下queue.next()這個方法,在注釋中我們看到,這個方法有可能會被阻塞,阻塞的原因是訊息佇列中沒有訊息。

Message next() {    int pendingIdleHandlerCount = -1; // -1 only during first iteration    int nextPollTimeoutMillis = 0;    for (;;) {        if (nextPollTimeoutMillis != 0) {            Binder.flushPendingCommands();        }        // We can assume mPtr != 0 because the loop is obviously still running.        // The looper will not call this method after the loop quits.        nativePollOnce(mPtr, 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 (false) Log.v("MessageQueue", "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("MessageQueue", "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;    }}

nativePollOnce(mPtr, nextPollTimeoutMillis)當輪詢沒有訊息時,會進行阻塞。訊息喚醒和阻塞機制將會在下一篇文章進行介紹,請大家關注。

最後對Handler做一下總結。從訊息的分發一直到訊息的處理,先後接觸到的幾個名詞有Looper、MessageQueue、Thread、Handler、Message。

  • Looper:負責初始化訊息佇列,並不斷的從訊息佇列中輪詢訊息。
  • MessageQueue : 是訊息佇列,存放在handler發送過來的訊息。
  • Thread:當前訊息佇列和looper所操作的場所或者說啟動並執行環境。
  • Handler:負責訊息的發送和處理。
  • Message:一個更新UI的訊息,由handler發出,由MessageQueue列隊。

訊息處理機制大概的一個處理過程如下:

關於輪詢的時候,阻塞和喚醒機制請看下一篇文章。

補充:在子線程中要更新ui的時候,可以這樣處理

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

著作權聲明:本文為博主原創文章,未經博主允許不得轉載(連絡方式:QQ312037487 郵箱:andywuchuanlong@sina.cn)。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.