Handler機制源碼學習

本文主要講的是Android的Handler消息機制。提到Handler我們應該都不會陌生,平常在開發中免不了要和Handler打交道,那Handler到底是怎樣工作的呢,那我們來簡單的梳理一下。

話不多說先放一張圖:

從上圖可以看出Handler機制中主要包含 Handler  Message  MessageQueue   Looper 

他們的工作流程是這樣的Handler把消息也就是Message發送到MessageQueue中,然後呢Looper一直在循環着把MessageQueue中的Message拿出來在交給Hanlder去處理。

1. Message類

Message的主要功能是進行消息的封裝,同時還可以指定消息的操作形式,Message類定義的變量和常用方法如下:

(1)public int what:變量,用於定義此Message屬於何種操作

(2)public Object obj:變量,用於定義此Message傳遞的信息數據,通過它傳遞信息

(3)public int arg1:變量,傳遞一些整型數據時使用

(4)public int arg2:變量,傳遞一些整型數據時使用

(5)public Handler getTarget():普通方法,取得操作此消息的Handler對象。

2、Looper類

Looper在消息機制中扮演的是消息循環的角色,具體的講就是它會不停的從MessageQueue中查看是否有新消息,如果有新消息就會立刻處理否則就一直阻塞在那裏。

從他的調用方式說起吧,我們都知道要先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));
    }

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

prepare方法其實就是new了Looper存在ThreadLocal中。至於這個prepareMainLooper 是給主線程創建Looper使用的在ActivityThread中可以自己去看看。

然後我們再看看它的構造方法都幹了什麼,

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

它new了一個MessageQueue,然後獲取了當前線程保存起來。

使用Looper的時候我們還會知道另外一個方法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();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);               

        boolean slowDeliveryDetected = false;

        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;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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();
        }
    }

loop是一個死循環,唯一跳出循環的方式是MessageQueue的next方法返回了null。當next方法 返回了新消息時Looper就會處理這條消息:msg.target.dispatchMessage(msg); 這裏的target就是發送這條消息的Handler對象這樣Handler發送的消息 最終又交給它的dispatchMessage方法來處理了。但是這裏的dispatchMessage方法是在創建Handler時所使用的looper中執行的,這樣就成功的切換到指定的線程中去執行了。

3、Handler類  

Handler是操作Message的一個類,先看一下它的構造方法。

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();  //關聯當前線程的looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue; //直接把關聯looper的MessageQueue作爲自己的
        mCallback = callback;
        mAsynchronous = async;
    }

在構造方法中先是獲取了當前線程的looper  並且把當前looper的MessageQueue作爲自己的。

那麼它是如何操作Message的呢?

我們都知道Handler要先把消息發送到MQ中去那我們就先看看怎麼樣發送的 這裏就說一下sendMessage吧其實還有Post系列的但是最終其實調的都是一個方法下面我們就看一看??


 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }    

//最終調用這個方法將消息放進MQ裏面
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);
    }

還記得我們之前看Message的時候裏面有個target嗎 到底在哪兒給他賦值的呢?

且看這個方法enqueueMessage()

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; //將Handler賦值給Message的Target
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

我們這就把消息發送出去了,此時我們的Looper也已經在不斷循環去找消息了它找到消息後會調用這個方法msg.target.dispatchMessage(msg)這個方法就在Handler裏面至此一個完整Handler消息機制就完成了。

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章