【原創】源碼角度分析Android的訊息機制系列(五)——Looper的工作原理

來源:互聯網
上載者:User

標籤:thread   tar   lstat   its   should   ring   nis   course   pre   

ι 著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

 

 Looper在Android的訊息機制中就是用來進行訊息迴圈的。它會不停地迴圈,去MessageQueue中查看是否有新訊息,如果有訊息就立刻處理該訊息,否則就一直等待。

Looper中有一個屬性:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

這也就解釋了,前面我們所說的我們可以通過ThreadLocal實現Looper線上程中的存取。

除此之外,還有兩個屬性需要注意:

final MessageQueue mQueue;final Thread mThread;

 

下面我們先看下Looper的建構函式:

    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

在建構函式中,建立了一個MessageQueue訊息佇列,並且將當前線程的對象儲存了起來。

 

接下來看loop方法,只有調用了loop方法後,訊息迴圈系統才真正地起到了作用。

    /**     * Run the message queue in this thread. Be sure to call     * {@link #quit()} to end the 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            final Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }             final long traceTag = me.mTraceTag;            if (traceTag != 0) {                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));            }            try {                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }             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();        }    }

loop方法中首先調用了myLooper方法:

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

myLooper方法會返回與當前線程相關聯的Looper對象。如果當前線程沒有關聯任何Looper對象的話,該方法則返回null。

查看loop方法的源碼,可以知道,噹噹前線程沒有關聯任何Looper對象時,loop方法會拋出運行時異常,提示當前線程中沒有Looper。若想解決該問題,可以在loop方法被調用前,先執行Looper.prepare()方法,建立一個looper對象。繼續看loop方法的源碼,可以看到該方法是一個死迴圈,唯一可以跳出該迴圈的方法就是queue.next()返回的對象為null。在上面的文章中,我們分析過,queue.next()即讀取MessageQueue中的訊息,next()方法返回null,說明MessageQueue中沒有Message,即該MessgaeQueue調用了quit方法。那麼何時MessageQueue會調用quit方法呢?來看下Looper的quit方法:

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

以及Looper的quitSafely方法:

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

Looper的quit方法和quitSafely方法都會導致MessageQueue調用quit方法,所以當不需要Looper的時候,建議調用Looper的quit()方法或quitSafely()方法,以避免loop方法無限迴圈下去。

要想知道Looper的quit方法和quitSafely方法的區別,我們看下MessgaeQueue的quit方法:

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

安全退出,則調用removeAllFutureMessagesLocked()方法,該方法會設定一個標記,當訊息佇列中的已有訊息全部處理完畢後才會安全退出;quit則會調用removeAllMessagesLocked(),直接退出。

下面接著看loop方法,重點看這一句:

msg.target.dispatchMessage(msg);

在Android的訊息機制概述中,我們已經說過,target是Message的一個屬性,其類型為Handler,msg.target也就是發送這條訊息的對象。由此一來,Handler發送的Message最終又交給了它自己來調用dispatchMessage方法來處理,但是dispatchMessage方法是在Looper的loop方法中被調用的,那麼Looper的loop方法是在哪裡執行的呢?在建立Handler時所在的線程中執行的。

ActivityThread(主線程)在建立時,會初始化Looper,所以我們可以在主線程中直接使用Handler,當需要更新UI時,可以通過Handler發送訊息,最後就可以回到主線程去更新UI啦,啦啦啦。

 

除此之外,Looper還提供了一些其他的方法,例如prepareMainLooper方法:

    /**     * Initialize the current thread as a looper, marking it as an     * application‘s main looper. The main looper for your application     * is created by the Android environment, so you should never need     * to call this function yourself.  See also: {@link #prepare()}     */    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }

該方法會執行個體化當前線程作為一個looper,但是是主線程的looper啦。Android系統會為我們建立主線程的looper,我們也不需要自己手動去調用該方法了。該方法的實質還是通過prepare方法實現的。

再如getMainLooper方法:

    /**     * Returns the application‘s main looper, which lives in the main thread of the application.     */    public static Looper getMainLooper() {        synchronized (Looper.class) {            return sMainLooper;        }    }

該方法使得我們可以在任何地方擷取到主線程的Looper了。

 

【原創】源碼角度分析Android的訊息機制系列(五)——Looper的工作原理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.