Android進階知識樹——Android消息隊列

一、概述

在安卓程序啓動時,會默認在主線程中 運行程序,那若是執行一些耗時的操做則UI就會處於阻塞狀態,出現界面卡頓的現象,再者用戶的多種操做,系統是如何作到一一處理的,系統又是如何管理這些任務的,答案這就是今天的主題Android的消息機制;bash

  • Android處理消息的方式——handler、Looper 和 MessageQueue
  1. Handler :負責將任務添加到隊列,執行結束後在主線程執行UI操做
  2. Looper :未綁定的線程開啓循環消息隊列,並獲取消息
  3. MessageQueue :任務隊列,保存發送的消息

二、源碼分析

對於Handler的使用方法此處不作介紹,在使用是隻要注意避免內存泄漏需建立靜態Handler,此處從執行過程和原理的角度簡單分析Handler的源碼,首先你們都知道在主線程中能夠直接使用Handler,而在子線程中不能夠,有沒有想過爲何呢?如今就先看看主線程究竟有什麼不一樣吧,從頭看安卓的入口方法ActivityThread.main()async

​
public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
      ...
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

​複製代碼
  • 上述代碼看出在程序啓動後,執行三個步驟:
  1. 調用Looper.prepareMainLooper();建立消息循環Looper
  2. 使用 thread.getHandler(); 獲取UI線程的Handler
  3. 使用 Looper.loop()開啓消息循環

Handler與消息隊列關聯,而消息隊列被封裝在Looper中,每一個Looper關聯一個線程,因此Handler的消息大體爲handler做爲一個消息處理器,將消息傳遞到消息隊列中,線程中的Looper逐個取出消息並執行,此處的線程即爲UI線程,ide

  • Handler最基本的用法是建立Handler 發送Message到隊列,如今看看建立Handler時都作了哪些事
​
 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;
    }

​複製代碼
  1. 調用Looper.myLooper():獲取Looper對象,若是爲空則拋出異常,而後調用lopper.mQuene開啓消息隊列
  • 查看myLooper()方法
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }複製代碼
  1. 在myLooper中看出,Looper從sThreadLocal中獲取,那何時設置進去的呢?另外從這也能夠看出來,只要sThreadLocal沒有Looper的實例就會拋出異常,sThreadLocal就是執行的線程,換句話說只要Handler所在的線程中沒有Looper的實例就會異常;
  • 接下來看看UI線程開始時建立的Looper對象的方法prepareMainLooper()
​public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }複製代碼
  • prepareMainLooper() 調用了prepare()方法:
​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));
    }複製代碼
  1. 看到了 sThreadLocal.set(new Looper(quitAllowed)),此處建立Looper對象設置進sThreadLocal中,是否是明白了上面的sThreadLocal。get()獲取的Looper是哪裏的了。

到這裏咱們介紹了Handler 和 Looper的建立,以及各自對線程的關聯,下面查看如何啓動消息循環獲取任務並執行,在入口方法的最後調用了Loop.loop()方法,下面查看Looper.loop()方法:oop

public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
...
 msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}複製代碼
  1. 調用myLooper()獲取Looper的對象,獲取消息隊列後調用next()依次取出其中的Message,注意這裏是個死循環
  2. 調用Messagemsg.target.dispatchMessage(msg)執行方法
  • 查看Message的源碼便知target實際是Handler的對象,說明繞了一圈最後仍是Handler的dispatchMessage(msg)處理邏輯
Handler target;
Runnable callback;
Message next;複製代碼
  • Handler的dispatchMessage(msg)方法
​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();
    }複製代碼

總結一下:looper從MessageQuene中依次取出Message,而後調用Msg綁定的Handler的dispatchMessage(msg)處理,dispatchMessage()中依次判斷執行:源碼分析

  1. 先判斷Message的Runable對象callback是否爲null,若不爲空使用handleCallback()即調用Runnable,run()執行
  2. 若callback爲空判斷mCallBack,若mCallBack不爲空則調用mCallBack.handleMessage()處理
  3. 若是上面二者都爲null,則執行handleMessage(),此處即爲建立Handler()重寫的handlerMessage()

到此Android中Handler的消息處理,從建立Handler、Looper和MessageQueue到消息的傳遞和事件的處理都介紹完了,下面咱們看看使用時如何發送消息到MessageQueue以及上述的callback、mCallBack什麼狀況下爲空?post

  • 建立的Handler後發送消息方法:sendMessage(Message msg)、post(Runnable r)
  1. 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) {
        MessageQueue queue = mQueue;
        
        return enqueueMessage(queue, msg, uptimeMillis);
    }

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;   // 此處設置target爲Handler對象
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }複製代碼

上面的執行過程代碼從上倒下一目瞭然,在enqueueMessage()中設置了Message.target即爲調用方法的Handler,因此在調用dispatchMessage()時,會調用Handler的handleMessage()處理ui

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

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;   // 設置Message的callback爲r
        return m;
    }複製代碼
  1. 調用getPostMessage()設置Message中的callback,因此callback就是發送的runnable,其他方法和sendMessage()一致。

Handler消息機制的原理和過程介紹完畢了,此時知道爲何不能在子線程建立handler嗎?拋出的異常在上述代碼中也出現了this

mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }複製代碼

當Looper.myLooper()取出爲空時拋出異常,而myLooper()中時從sThreadLocal.get()中獲取,即此時sThreadLocal中沒有設置Looper對象,因此也就沒有後面的隊列等操做,從上面知道建立並設置Looper對象是在prepare()方法中,因此要在子線程中使用Handler要先調用Looper.prepare()建立Looper對象,後調用Looper.loop()開啓消息循環。spa

相關文章
相關標籤/搜索