Android Handler的原理

1. 簡介

衆所周知主線程(也叫UI線程,進行UI的更新)是不能夠進行任何耗時操做,好比耗時的計算,訪問服務器等等。那若是開啓子線程進行復雜計算之後想要把結果傳遞給主線程進行結果的展現(UI更新),這個時候該怎麼辦比較好呢。java

有兩種方法,第一種是用AsyncTask,另外一種就是要講的Handler了。固然AsyncTask和Handler用途和原理有所不一樣,因此應該根據場景選擇使用。服務器

Handler是什麼?Handler是在Android中用於消息的傳遞。Android中的主線程會維護一個Looper和MessageQueue,來保證內部的信息傳遞,固然咱們也能夠使用它們來完成咱們的邏輯。簡單說明一下Looper和MessageQueue。async

Looper: 它的主要做用是管理維護MessageQueue,從隊列中取出消息傳遞給Handler來處理。 MessageQueue: 它的主要做用是存儲消息,當隊列爲空時進行隊列阻塞。ide

2. 消息傳遞處理流程

  1. Handler在線程A中發出消息,傳遞到MessageQueue中進行儲存。
  2. Looper在線程B中取出MessageQueue中的消息。
  3. Looper在線程B中把消息傳遞給Handler。
  4. Handler在線程B中處理消息。

一個Looper對應一個線程。若是在主線程中發送的Message存儲到共同維護的MessageQueue中,在另外一個子線程的Looper進行loop(用於去消息的方法)取消息而後傳遞給Handler,則Handler會在Looper所在的線程進行消息的處理。oop

整個流程以及關係以下。 源碼分析

3. 源碼分析

  1. 首先從Handler的源碼開始分析,以下。
public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
        //獲取Looper對象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //獲取Looper對象的mQueue屬性,mQueue 就是MessageQueue對象。
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
複製代碼

Handler中會經過Looper.myLooper()來獲取獲取當前線程的Looper,若是爲空則會拋出異常。因此在子線程中建立Handler,則首先要經過Looper.prepare()來建立Looper,再經過Handler傳遞消息。值得注意的是主線程中已經默認建立了Looper,因此在主線程不要再單首創建Looper,若是建立則會拋出異常。post

  1. 再看一下Looper.myLooperLooper.prepare()的源碼。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    //建立當前線程的Looper對象
    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對象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
複製代碼

經過prepare()方法就能夠知道每一個線程中只能有一個Looper,這也是爲何不能在主線程建立Looper的緣由。myLooper()來獲取當前線程的Looper對象。 還有值得注意的細節是,管理Looper的數據類型是ThreadLocal,它一個線程內部的數據存儲類,經過它存儲的數據只有在它本身的線程才能獲取到,其餘線程是獲取不到的。與之相對應的是ThreadGlobal是能夠獲取全局任意一個線程。因此在這裏sThreadLocal.get()是獲取當前線程的數據的意思。ui

  1. 接下來繼續看一下Looper。
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed); //實例化MessageQueue對象
        mThread = Thread.currentThread(); //當前線程
    }
複製代碼

Looper對象中建立了MessageQueue(消息隊列,用於存儲消息)對象以及獲取了當前的線程。由於Looper的建立是私有的,因此外界想要獲取Looper對象只能經過Looper.prepare()方法。this

  1. 發出消息(sendMessage)

無論是handler.sendMessage(msg)handler.post(runnable)最底層的方法都是下面的 enqueueMessage()方法。經過這個方法把消息存到了MessageQueue中。spa

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; //this是Handler對象。
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把消息存到MessageQueue,並返回存儲成功失敗的結果
        return queue.enqueueMessage(msg, uptimeMillis);
    }
複製代碼
  1. 取出消息

消息的取出用的是Loooper.loop()方法。首先看一下代碼。

public static void loop() {

        final MessageQueue queue = me.mQueue;
        //死循環
        for (;;) {
            //從MessageQueue中取出一條消息
            Message msg = queue.next(); 
            if (msg == null) {
                // 若是沒有消息了,直接跳出循環
                // 可是正常狀況下這段並不會被調用
                return;
            }
            //把消息交給Handler處理。
            msg.target.dispatchMessage(msg);
        }
    }
複製代碼

會發現上面代碼中有一個死循環,經過這個死循環不斷的從MessageQueue中取出消息。當queue.next()沒有獲取到消息則會被阻塞,知道有消息被存放到MessageQueue中。當獲取到了消息,則經過dispatchMessage()來分發給Handler處理消息。

  1. 處理消息

消息最後到達了Handler的dispatchMessage()方法。讓咱們看一下代碼。

public void dispatchMessage(Message msg) {
        // 若是Message有本身的callback,就由Message的callback處理
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
             //若是Handler有本身的mCallback,就由Handler的mCallback處理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //默認的處理消息的方法
            handleMessage(msg);
        }
    }
複製代碼

經過代碼就能夠知道消息處理的優先級。

  1. 優先級最高的是message中本身的回調,這裏的本身的回調是handler.post(runnable)中的runnable(msg.callback = runnable)。
  2. 若是message中沒有設置回調,則判斷Handler中是否有本身的回調。這裏的回調是咱們在Handler中重寫的handlerMessage()方法。
  3. 最後是默認的內部的handleMessage()方法。

源碼的分析差很少了,下一個文章是Handler的具體的使用方法和示例。

Handler使用方法文章:juejin.im/post/5e2c04…

相關文章
相關標籤/搜索