Android Handler 消息機制

目錄

 

一、Handler的簡單使用

二、ThreadLocal 工作原理

三、Looper類

四、Handler 類

五、MessageQueue類

 流程

相關問題


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

二、ThreadLocal 工作原理

Handler創建時會採用當前線程的Looper來構造消息循環,ThreadLocal可以在不同線程中互不干擾地存儲並提供數據,通過ThreadLocal 可以輕鬆獲得每個線程的Looper。ThreadLocal 是一個線程內部的數據存儲類,通過它可以在指定線程中存儲數據,而且只能在指定線程獲取數據,其他線程無法獲取到數據。不同線程訪問同一個ThreadLocal 的get方法時,ThreadLocal 內部會從各自的線程中取出一個數組,然後從數組中根據當前ThreadLocal的索引去查找對應的值,不同線程的數組是不同的,所以不同線程間數據互不干擾。

ThreadLocal是一個泛型類,定義爲 public class ThreadLocal<T>,它的set方法:

public void set(T value){
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
}

 在上面的set方法中,首先通過getMap方法來獲取當前線程的ThreadLocalMap,在Thread類內部有一個成員專門用於存儲線程的ThreadLocal數據:ThreadLocalMap threadLocals,如果threadLocals的值爲null,那麼就需要進行初始化,然後再將值存儲。ThreadLocalMap是ThreadLocal的內部類,在內部有一個Entry數組(Entry是ThreadLocalMap的內部類),ThreadLocal的值就存在這個數組中。接下來看下get方法。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);     //拿到當前線程的ThreadLocalMap
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);    // 以該ThreadLocal對象爲key取value
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

相關文章:【ThreadLocal】深入JDK源碼之ThreadLocal類

ThreadLocal源碼分析

 

三、Looper類

This class contains the code required to set up and manage an event loop based on MessageQueue.  APIs that affect the state of the queue should be defined on MessageQueue or Handler rather than on Looper itself. For example, idle handlers and sync barriers are defined on the queue whereas preparing the thread, looping, and quitting are defined on the looper.

該類包含設置和管理基於MessageQueue的事件循環所需的代碼。影響隊列狀態的api應該在MessageQueue或Handler上定義,而不是在Looper本身上定義。例如,在隊列上定義空閒處理程序和同步屏障,而在looper上定義線程準備、循環和退出。

成員變量:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    private Printer mLogging;
    private long mTraceTag;

    /* If set, the looper will show a warning log if a message dispatch takes longer than time. */
    private long mSlowDispatchThresholdMs;

其中sThreadLocal 保存各個線程的Looper。Looper通過prepare()方法創建。

public static void prepare() {
        prepare(true);
    }
    //quitAllowed爲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));
    }

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

//創建主線程Looper,Android中會主動調用此方法,所以 UI 線程默認有Looper
public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

在app啓動時會在AcitvityThread的main方法中調用prepareMainLooper()方法完成主線程Looper初始化。

類中還有一系列get方法來返回線程的消息隊列,Looper,mainLooper,線程等。以及退出方法。

//當不再處理消息隊列消息時退出。退出後再往隊列添加信息會返回false

//此方法是不安全的,在退出時可能還有信息沒有發送
public void quit() {
        mQueue.quit(false);
    }


//在退出前處理完隊列中消息,但是掛起延遲消息不會被處理
public void quitSafely() {
        mQueue.quit(true);
    }

 接下來是最重要的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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

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

首先獲取線程的消息隊列和Looper,進行一些初始化後進行for死循環,調用queue.next()獲取隊列中消息,當沒有消息時阻塞,如果有消息時則處理,即msg.target.dispatchMessage(msg)。 msg.target是發送這條消息的Handler對象,在放入隊列時設置,這樣消息就分發給dispatchMessage方法處理,此方法是在創建Handler對象所在線程執行,這樣就完成了線程切換。注意到當消息爲null時退出loop方法,而當Looper的quit方法被調用時,Looper就會調用消息隊列的quit()或者quitSafely方法來退出隊列,當隊列被標記爲退出狀態時,它的next方法就會返回null。

 

四、Handler 類

Handler是用來發送和處理{@link Message},Runnable和線程的{@link MessageQueue}關聯的對象。每個Handler實例與單個線程和該線程的消息隊列相關聯。當您創建一個新的Handler時,它被綁定到創建它的線程和創建它的線程的消息隊列——從那時起,它會將消息和Runnable發送到該消息隊列,在它們從消息隊列發出時執行它們。

用途:調度消息和將來某個時候執行的Runnable;入隊,要在不同於您自己的線程上執行;

其成員變量:

// 實例化Handler時可以使用回調接口,以避免必須實現自己的Handler子類(即重寫handleMessage方法)
public interface Callback {
        public boolean handleMessage(Message msg);
    }

    final Looper mLooper;
    final MessageQueue mQueue; // 消息隊列
    final Callback mCallback;
    final boolean mAsynchronous;
    IMessenger mMessenger;

構造方法,有多個重載:

public Handler() {
        this(null, false);
    }

public Handler(Callback callback) {
        this(callback, false);
    }

public Handler(Looper looper) {
        this(looper, null, false);
    }

public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
public Handler(boolean async) {
        this(null, async);
    }

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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

接下來看一下Handler傳遞消息相關方法:

 public final boolean post(Runnable r){
     return sendMessageDelayed(getPostMessage(r), 0);
 }

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

 public final boolean postAtTime(Runnable r, long uptimeMillis){
     return sendMessageAtTime(getPostMessage(r), uptimeMillis);
 }
 
 public final boolean sendEmptyMessage(int what){
     return sendEmptyMessageDelayed(what, 0);
 }

 public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
     return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
 }
 
 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
 }
 
 public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  }

 public final boolean postDelayed(Runnable r, long delayMillis){
     return sendMessageDelayed(getPostMessage(r), delayMillis);
 }

 public final boolean postDelayed(Runnable r, Object token, long delayMillis){
     return sendMessageDelayed(getPostMessage(r, token), delayMillis);
 }

 public final boolean postAtFrontOfQueue(Runnable r){
     return sendMessageAtFrontOfQueue(getPostMessage(r));
 }
 
 public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
 }
 ....

 傳遞消息類型有兩種,一種是Runnable,一種是 Message,傳遞方式有延遲發送,按時發送。也可發送空Message,也可以將消息放到隊列前面。可以看到最終發送調用sendMessageDelayed,sendMessageAtTime方法,消息放到隊列調用enqueueMessage方法:

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

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方法,其中設置消息的target爲當前Handler,並調用當前Handler所屬線程消息隊列的 enqueueMessage 方法。

 接下來看下處理消息的方法:

public void dispatchMessage(Message msg) {
        // 是否傳遞 runnable
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 是否設置Callback(如果沒有則實現了子類)
            if (mCallback != null) {
                // Callback的handleMessage方法在初始化Handler時傳入的Callback中實現
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

// 執行傳遞過來runnable
private static void handleCallback(Message message) {
        message.callback.run();
}

// 由子類重寫
public void handleMessage(Message msg) {
}

 

handleCallback處理Runnable類型消息,在其中執行其run方法,CallBack及Handler的handleMessage方法均由用戶實現。

 

五、MessageQueue

    MessageQueue用來存儲傳遞的消息,重點關注enqueueMessage和next方法,enqueueMessage:

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) {
                // 第一個消息,如果隊列阻塞則喚醒
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 判斷是否需要喚醒隊列(目前阻塞及消息是隊列中最早的異步消息則喚醒)
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                // 將消息插入隊列
                for (;;) {
                    prev = p;
                    p = p.next;
                    // 當前位置爲空或者當前消息when比待插入when大跳出循環
                    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;
    }

 根據傳遞消息設置的when找到位置並插入隊列(詳情看註釋)。next方法:

 

 Message next() {
        // 如果消息循環已經退出並已被釋放,則返回此處。
        // Looper的loop方法會結束
        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) {
                    // 查找隊列中的下一個異步消息。(設置屏障)
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                       //下一條消息還沒有準備好。設置一個超時,在它準備好時喚醒它。
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 獲得一個消息 出隊
                        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 {
                    // 沒有更多消息
                    nextPollTimeoutMillis = -1;
                }

                // 現在所有掛起的消息都已處理完畢,處理quit消息
                if (mQuitting) {
                    dispose();
                    return null;
                }

                //如果第一次空閒,則獲得要運行的空閒組的數量。
                //mIdleHandlers僅在隊列爲空或隊列中的第一個消息(可能是一個障礙)將在將來處理時                
                //運行。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 沒有idle handlers 需要執行.  循環並等待消息.
                    mBlocked = true;
                    continue;
                }

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

            // 執行 idle handlers.
            //我們只在第一次迭代中到達這個代碼塊.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // 釋放對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);
                    }
                }
            }

            // 將空閒處理程序計數重置爲0,以便不再運行它們.
            pendingIdleHandlerCount = 0;

            // 在調用空閒處理程序時,可能已經傳遞了一條新消息
            //因此,請返回並再次查找未等待的消息。
            nextPollTimeoutMillis = 0;
        }
    }

 循環檢測隊列中是否有消息需要出隊,有則返回。

 流程

  通過Handler傳遞消息,設置其target對象爲發送的Handler,然後放入同線程的MessageQueue中,線程中的Looper會一直將MessageQueue的消息取出並調用其target對象(即發送消息的Handler)的dispatchMessage方法分發並處理,Looper運行在創建時的線程,這樣就完成了線程切換。(下圖來源於網絡)

相關問題

1.內存泄漏

 java 匿名內部類和非靜態內部類會自動隱式持有當前類的外部引用,所以當使用匿名內部類初始化Handler時,持有activity/Fragment 引用,當activity/fragment 退出 但還有消息未處理時,會造成內存泄漏(主線程的Looper對象會伴隨該應用程序的整個生命週期。當在主線程中初始化Handler時,該Handler就會自動和主線程Looper的消息隊列關聯起來。所有發送到消息隊列的消息Message都會擁有一個對Handler的引用)

解決方法可在退出前清空消息隊列,調用Handler方法removeCallbacksAndMessages(null)或者採取靜態內部類,並持有外部類弱引用 方式。

2.Looper.loop()中死循環爲什麼不會造成應用卡死?網上說法如下:

 在MessageQueue的next方法,在for循環中,會調用nativePollOnce方法,如果消息隊列中沒有消息存在nativePollOnce就不會返回,阻塞隊列。在MessageQueue的方法中將消息插入隊nativePollOnce列後,如果狀態位needWake爲true則調用nativeWake方法,在此方法中觸發上面提到的nativePollOnce方法返回。nativePollOnce方法和nativeWake方法都是本地方法,源碼在native層。

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