Handler機制

源碼第四篇--Android應用程序是經過消息來驅動的,Android某種意義上也能夠說成是一個以消息驅動的系統,UI、事件、生命週期都和消息處理機制息息相關,而且消息處理機制在整個Android知識體系中也是尤爲重要,在源碼分析的路上Handler機制是必不可少的。若是你對Handler機制還不是特別瞭解的話,能夠靜下心來看一看這篇。安全

Handler機制

相關概念:在理解Handler機制以前,須要先熟知跟Hanlder相關的幾個類:Handler、Message、Message Queue、Looper,這幾個類之間的關係以下圖:bash

  1. Looper:線程是一段可執行的代碼,當可執行代碼執行完成後,線程生命週期便會終止,線程就會退出,而主線程要是自動退出,那還玩個什麼,所以此時就用到了Looper,在主線程中調用Looper.prepare()...Looper.loop(),在loop方法裏會執行一段for (;;) {}代碼,保證主線程不會自動退出,具體下面再說,主線程會在這段死循環中不斷等其餘線程給它發消息(消息包括:Activity啓動,生命週期,更新UI,控件事件等),一有消息就根據消息作相應的處理,Looper的另一部分工做就是在循環代碼中會不斷從消息隊列挨個拿出消息給主線程處理。

2.MessageQueue:就是一個隊列,特性就是「先進先出,後進後出」,每個Looper都會維護這樣一個隊列,並且僅此一個,這個隊列的消息只能由該線程處理。多線程

3.Handler:Handler在sendMessage的時候會往消息隊列裏插入新消息。Handler的另一個做用,就是能統一處理消息的回調。併發

接下來開始講解Hanldre機制的源碼,源碼將分爲3部分說明,包括程序啓動初始化、Loop的輪詢機制、Handler消息的發送async

  • 程序初始化:應用程序的開始入口是從ActivityThread.main入口的,具體可看上面Activity的啓動流程中,假設的第一種:應用第一次啓動
// 程序入口函數
public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        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>");
        
        // 主線程Loop初始化
        Looper.prepareMainLooper();
        // 初始化資源管理器
        ActivityThread thread = new ActivityThread();

        /**
         *  attach方法會建立一個Binder線程(具體是指ApplicationThread,該Binder線程會經過向
         *   Handler將Message發送給主線程)。咱們以前提到主線程最後會進入無限循環當中,
         *   在沒有在進入死循環以前會在這裏建立Binder線程,這個線程會接收來自系統服務
         *   發送來的一些事件封裝了Message併發送給主線程,主線程在無限循環中從隊列裏拿到這
         *   些消息並處理。(Binder線程發生的消息包括LAUNCH_ACTIVITY,PAUSE_ACTIVITY等等)
         *
         * 而且會在該方法中完成Application對象的初始化,而後調用Application的onCreate()方法
        */

        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.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
複製代碼

所以在程序入口函數中,就完成了Looper的主線程初始化以及輪詢方法的運行。ide

  • Looper的輪詢機制,接下來看看主線程Looper的初始化,以及輪詢loop方法,從程序入口方法中看到主線程的Looper的初始化調用prepareMainLooper方法完成。
// 主線程Looper的初始化
    public static void prepareMainLooper() {
        prepare(false); // 實際上調用此方法初始化Looper
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper(); // 設置主線程Looper
        }
    }
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 將Looper設置到ThreadLocal中,具體後面再將
        sThreadLocal.set(new Looper(quitAllowed));
    }
    // Looper構造方法
    private Looper(boolean quitAllowed) {
        // Looper在初始化時,會實例一個消息隊列,此時Looper持有MessageQueue的引用
        mQueue = new MessageQueue(quitAllowed);
        // 當前Looper所在線程
        mThread = Thread.currentThread();
    }
    
    public static void loop() {
        // 獲取當前Looper的實例對象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 獲取當前Looper的消息隊列
        final MessageQueue queue = me.mQueue;
        ...
        // 主線程開啓無限循環模式
        for (;;) {
            // 獲取隊列中下一條消息並刪除,可能會線程阻塞
            Message msg = queue.next();
            if (msg == null) {
                return;
            }
            ...
            try {
                //分發Message,
                //這個Message會持有這個Handler的引用,並放到本身的target變量中,這樣就能夠回調咱們重寫的handler的handleMessage方法。
                /**
                 * 分發消息:
                 *  msg.target是一個Handler對象,在Handler調用sendMessage方法時會設置此變量
                 *   所以就能夠作到哪一個Handler發送的消息,就會經過哪一個Handler的分發消息方法
                 *   dispatchMessage將消息從新回調到該Hanlder中
                */
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
            // 將Message回收到消息池,下次要用的時候不須要從新建立,obtain()就能夠了。
            msg.recycleUnchecked();
        }
    }
複製代碼

prepareMainLooper()方法實際上就是new了一個Looper實例並放入Looper類下面一個static的sThreadLocal靜態變量中,同時給sMainLooper賦值,給sMainLooper賦值是爲了方便經過Looper.getMainLooper()快速獲取主線程的Looper,sMainLooper是主線程的Looper可能獲取會比較頻繁,避免每次都到 sThreadLocal 去查找獲取。loop()方法 == 消息出隊 + 分發給對應的Handler實例函數

注:此處有一個重要的概念ThreadLocal,後面具體分析oop

  • Handler:先接上面個Looper的loop方法,看看Handler的dispatchMessage分發消息方法,再看看Handler的初始化以及發送消息sendMessage方法
public void dispatchMessage(Message msg) {
        // 若msg.callback屬性不爲空,則表明使用了post(Runnable r)發送消息
        // 則執行handleCallback(msg),即回調Runnable對象裏複寫的run()
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 若msg.callback屬性爲空,則表明使用了sendMessage(Message msg)發送消息
            // 則執行handleMessage(msg),即回調複寫的handleMessage(msg)
            handleMessage(msg);
        }
    }
    
    // 直接回調Runnable對象裏複寫的run()方法
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
    // 注:handleMessage爲空方法,在建立Handler實例時會複寫該方法
    public void handleMessage(Message msg) {
    }
    
    // Handler構造方法1
    public Handler() {
        // 調用Handler構造方法6
        this(null, false);
    }
    // Handler構造方法2
    public Handler(Callback callback) {
        // 調用Handler構造方法6
        this(callback, false);
    }
    // Handler構造方法3
    public Handler(Looper looper) {
        // 調用Handler構造方法7
        this(looper, null, false);
    }
    // Handler構造方法4
    public Handler(Looper looper, Callback callback) {
        // 調用Handler構造方法7
        this(looper, callback, false);
    }
    // Handler構造方法5
    public Handler(boolean async) {
        // 調用Handler構造方法6
        this(null, async);
    }
    // Handler構造方法6
    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構造方法7
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
    // sendMessage方法
    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);
    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        // 獲取Looper中的隊列
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        // 將消息壓入Looper中的隊列中
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // 設置消息的target變量爲本Handler
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
複製代碼

dispatchMessage方法會先判斷msg.callback是否爲空,判斷出是該消息msg是經過post發送出的仍是 sendMessage方法發送出的,從而在對消息進行處理。Handler有衆多構造方法,不過最終調用的不過就是構造方法6和7兩個,這倆構造方法的區別之處就在於第一個參數,是否自定義設置Looper,若步自定義設置Looper則默認使用主線程的Looper。sendMessage方法從源碼中能夠看到最終是調用 sendMessageAtTime方法將消息壓入Looper中的隊列的,而又是在壓入隊列enqueueMessage方法中設置的消息msg的target變量的Handler值。源碼分析

MessageQueue中的源碼就不分析啦,有興趣的小夥伴自行查看吧。再放上一張Handler、Message、Message Queue、Looper之間的邏輯圖post

  • 最後來看看前面說的ThreadLocal:線程本地存儲區(Thread Local Storage,簡稱爲TLS),每一個線程都有本身的私有的本地存儲區域,不一樣線程之間彼此不能訪問對方的 TLS區域。這裏線程本身的本地存儲區域存放是線程本身的Looper。
public final class Looper {
    // sThreadLocal 是static的變量,能夠先簡單理解它至關於map,key是線程,value是Looper,
    //那麼你只要用當前的線程就能經過sThreadLocal獲取當前線程所屬的Looper。
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //主線程(UI線程)的Looper 單獨處理,是static類型的,經過下面的方法getMainLooper() 
    //能夠方便的獲取主線程的Looper。
    private static Looper sMainLooper; 

    //Looper 所屬的線程的消息隊列
    final MessageQueue mQueue;
    //Looper 所屬的線程
    final Thread mThread;
    ......
}
複製代碼

說到ThreadLocal,不得不說下ThreadLocalMap和Thread

1.ThreadLocal類用於存儲以線程爲做用域的數據,線程之間數據隔離。

2.ThreadLocalMap類是ThreadLocal的靜態內部類,經過操做Entry來存儲數據。

3.Thread類比較經常使用,線程類內部維持一個ThreadLocalMap類實例(threadLocals)。

handler消息機制中的Looper就存儲在ThreadLocal中,也正是由於Looper的建立是依賴當前線程的,一個線程只能有惟一一個Looper,因此Looper使用於存儲在ThreadLocal中。ThreadLocal類會根據當前線程存儲變量數據的副本,每個線程之間數據副本互不干擾,實現線程之間的數據隔離。

// ThreadLocal類:
    public void set(T value) {
        // 當前線程
        Thread t = Thread.currentThread();
        // 獲取當前線程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    ThreadLocalMap getMap(Thread t) {
        // 返回當前線程的ThreadLocalMap
        return t.threadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
// Looper類
    // Looper初始化時會調用該方法,上面Handler機制Hanlder部分提到過
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 將Looper設置到ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }
複製代碼

Looper類中的prepare方法,再結合ThreadLocal類中的set方法,能夠看出Looper實例是綁定到線程內部的ThreadLocalMap成員變量中的,所以一個線程只能綁定一個Looper,並且因爲每一個Thread都會維護一個ThreadLocal.ThreadLocalMap類,所以這就隔絕了多線程間通信混亂的問題,保證了絕對線程安全。 若Thread中的ThreadLocalMap爲空,則在set方法中會create建立。

此時就延申到了ThreadLocal的用處啦,簡單說一下:當某些數據是以線程爲做用域而且不一樣線程具備不一樣的數據副本的時候,就能夠考慮採用ThreadLocal。如今來張Thread、ThreadLocalMap和 ThreadLocal關係圖:

至此Handler機制完成。

參考文章:

blog.csdn.net/singwhatiwa…

www.jianshu.com/p/02962454a…

...

注:如有什麼地方闡述有誤,敬請指正。

相關文章
相關標籤/搜索