Handler、Looper與MessageQueue源碼分析

Android中能夠經過Handler來更新主線程中UI的變化,更新UI只能在主線程中進行更新,而爲了讓其餘線程也能控制UI的變化,Android提供了一種機制HandlerLooperMessageQueue一同協做來達到其餘線程更新UI的目的。git

通常咱們會在主線程中經過以下方法定義一個Handlergithub

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            tv.setText("mHandler change UI");
            super.handleMessage(msg);
        }
    };

通常都見不到LooperMessageQueue的,那麼它們都是在哪裏調用與如何協做的呢?在主線程不會顯式的調用Looper而是會在ActivityThread.main方法中默認調用。app

ActivityThread.main

public static void main(String[] args) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
 
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
 
        Environment.initForCurrentUser();
 
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
 
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
         TrustedCertificateStore.setDefaultUserDirectory(configDir);
 
        Process.setArgV0("<pre-initialized>");
 
        Looper.prepareMainLooper();//建立Looper
 
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
 
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
 
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
 
        // End of event ActivityThreadMain.
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();//開啓Looper循環
 
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

如上代碼,調用了Looper.prepareMainLooper()方法,在主線程中建立了一個Looper,不信的話咱們再查看該方法作了什麼async

Looper

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並賦給sThreadLocal
    }
 
    /**
     * 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();
        }
    }
     
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

prepareMainLooper方法中調用了prepare而經過prepare會發現它其實就是建立了一個Looper,並把它賦給了sThreadLocal。同時能夠經過myLooper方法獲取當前線程中的Looper。再來看下new Looper(quitAllowed)初始化了什麼ide

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

在這裏咱們終於看到了MessageQueue了,它建立了一個MessageQueue。該消息隊列就是用來保存後續的Message。再回到ActivityThread.main方法中,發現它調用了Looper.loop()是用來開啓Looper循環的,監聽消息隊列MessageQueue中的消息。oop

loop

咱們來看下Looper.loop()的源碼:post

public static void loop() {
        final Looper me = myLooper();//獲取Looper
        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);//經過Handler分發消息
            } 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中首先獲取了當前所在線程的Looper,同時也獲取到了Looper中的MessageQueue,說明Looper已經與當前的線程進行了綁定。在後面開啓了一個for的死循環,發現它作的事件是不斷的從消息隊列中取出消息,最後都交給msg.target調用它的dispatchMessage方法,那麼target又是什麼呢?咱們進入Messageui

Message

/*package*/ int flags;

    /*package*/ long when;
    
    /*package*/ Bundle data;
    
    /*package*/ Handler target;
    
    /*package*/ Runnable callback;
    
    // sometimes we store linked lists of these things
    /*package*/ Message next;

發現它就是咱們熟悉的Handler,說明最後調用的就是Handler中的dispatchMessage方法,對消息的分發處理。這樣一來Handler就經過Looper聯繫上了Looper所綁定的線程,即爲主線程。this

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());
            }
        }
 
        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,同時也獲取了Looper中的消息隊列。固然若是所處線程的Looper爲空的話就會拋出異常,這就解釋了爲何在非主線程中建立Handler要分別調用Looper.prepareLooper.loop而主線程則不須要,由於它默認已經調用了。spa

dispatchMessage

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

回到前面,對於dispatchMessage的處理,首先判斷msg.callback是否爲空,這裏callback經過上面的Message應該能知道他就是一個Runnable,若是不爲空則直接調用Runnablerun方法。不然調用HandlerhandleMessage方法.而這個方法相信你們已經很熟悉了,對事件的處理都是在這個方法中執行的。由於經過前面咱們已經知道了Handler已經聯繫上了主線程,因此handleMessage中的處理天然相對於在主線程中進行,天然也能更新UI了。經過這裏咱們能把Looper比做是一個橋樑,來鏈接Looper所在的線程與Handler之間的通訊,同時管理消息隊列MessageQueue中的消息。那麼前面的Runnable又是如何不爲空的呢?咱們使用Handler有兩種方法,一種是直接建立一個Handler而且重寫它的handleMessage方法,而另外一種能夠經過Handler.post(Runnable)來使用,這樣事件的處理天然就在run方法中實現。

上面介紹了Handler是如何聯繫上了須要操做的線程與對消息是如何取出與處理的。下面來談談消息是如何放入到Looper中的MessageQueue中的。

 sendMessageAtTime

經過Handler發送消息的方式不少,例如:sendMessagesendEmptyMessagesendMessageDelayed等,其實到最後他們調用的都是sendMessageAtTime方法。因此仍是來看下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);
    }

sendMessageAtTime則就是調用了enqueueMessage操做,看這方法名就知道是入隊列操做了。

enqueueMessage

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

果不其然直接調用了MessageQueue中的queue.enqueueMessage(msg, uptimeMillis)將消息加入消息隊列,同時這段代碼msg.target = this 將當前的Handler賦給了msg.target,這就是前面所說的Looper.loop方法中調用的Handler。這樣就把消息放到了MessageQueue中,進而經過前面所講的loop來取出消息進行相應的處理,這樣就構成了整個對消息進行處理的系統。這也是使用Handler內部所發生的原理。好了HandlerLooperMessageQueue它們之間的聯繫基本就是這些了。我也簡單畫了張圖但願有所幫助

圖片描述

總結

來總結下它們之間的流程。首先建立Handler而在Handler所處的線程中必需要有一個Looper,若是在主線程中默認幫咱們實現了,其餘線程必須調用Looper.prepare來建立Looper同時調用Looper.loop開啓對消息的處理。每一個Looper中都有一個MessageQueue它是用來存儲Message的,Handler經過post或者send..等一系列操做經過Looper將消息放入到消息隊列中,而Looper經過開啓一個無限的循環來一直監聽着消息的處理,不斷從MessageQueue中取出消息,並交給與當前Looper所綁定的handlerdispatchMessage進行分發,最後根據狀況調用Runnablerun或者HandlerHandlerMessage方法對消息進行最後的處理。

其它分享:https://idisfkj.github.io/arc...

關注

clipboard.png

相關文章
相關標籤/搜索