Android的Handler訊息機制 解析

YuanchaoLi發表於2019-04-01

Android的Handler訊息機制

實現原理

  1. 主執行緒會自動呼叫Looper.prepareMainLooper和Looper.loop,具體是在ActivityThread中main方法中呼叫的。
public static void main(String[] args) {
    ......省略無關程式碼
    
    // 主執行緒的Looper相關準備工作
    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    // 生成主執行緒
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    
    // 拿到主執行緒的Handler,並將主執行緒的Looper繫結到Handler中
    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();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製程式碼
  1. 子執行緒使用Handler時,首先需要呼叫Looper.prepare,prepare方法中,new一個Looper物件,存入ThreadLocal;在Looper的構造中,會new一個MessageQueue,繫結到當前Looper中。
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // new一個Looper物件
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    // new一個MessageQueue,繫結到Looper中
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
複製程式碼
  1. 建立Handler例項,在Handler構造中,若傳入Looper,則將傳入的Looper繫結給Handler的Looper,並將傳入Looper的MessageQueue也繫結給Handler的MessageQueue。若不傳入Looper,則構造中,直接取當前執行緒的Looper以及該Looper的MessageQueue和handler繫結。
// 建立Handler選擇無參構造,會走到這裡
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());
        }
    }
    // 拿到當前執行緒的Looper,繫結到Handler中
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    // 拿到當前執行緒Looper的MessageQueue,繫結到Handler中
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

// 建立Handler選擇有參構造,會走到這裡
public Handler(Looper looper, Callback callback, boolean async) {
    // 將傳入的Looper以及該Looper的MessageQueue繫結到Handler中,讓Handler在Looper所在的執行緒環境中
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
複製程式碼
  1. 接著呼叫Looper.loop,拿到myLooper,也就是從ThreadLocal中取,在拿到myLooper的MessageQueue,對MessageQueue死迴圈,MessageQueue.next()獲取訊息,沒有訊息則掛載。有訊息時,會呼叫message的target的dispatchMessage方法分發訊息。dispatchMessage方法中,首先判斷message是否有回撥,有則直接將新訊息傳遞給回撥介面的run方法中。若message沒有回撥,則再判斷handler是否有回撥,有回撥,則將新訊息傳遞給回撥介面的handleMessage方法中。若handler也沒有回撥,則將訊息傳遞給handler的handleMessage公開方法中。外部重寫該方法即可接收處理新訊息。
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的MessageQueue
    final MessageQueue queue = me.mQueue;

    ......省略無關程式碼
    
    // 對MessageQueue死迴圈
    for (;;) {
        // MessageQueue.next()獲取訊息
        Message msg = queue.next(); // might block
        // 沒有訊息則掛載
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        ......省略無關程式碼
        
        try {
            // 有訊息時,呼叫message的target的dispatchMessage方法分發訊息
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
        ......省略無關程式碼
        
        msg.recycleUnchecked();
    }
}

// Handler分發訊息
public void dispatchMessage(Message msg) {
    // Message有回撥,則直接將新訊息傳遞給回撥介面的run方法中
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // Handler有回撥,則將新訊息傳遞給回撥介面的handleMessage方法中
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 將訊息傳遞給handler的handleMessage公開方法中
        handleMessage(msg);
    }
}
複製程式碼
  1. hander.sendMessage,會呼叫enqueueMessage方法,將當前handler賦值給Message的target,然後呼叫handler的MessageQueue的enqueueMessage方法,內部會將新的message新增進MessageQueue中。此時,Looper中MessageQueue會被喚醒,迴圈獲取到新訊息做下一步處理。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 將當前handler賦值給Message的target
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 呼叫MessageQueue的enqueueMessage方法,內部會將新的message新增進MessageQueue連結串列中
    return queue.enqueueMessage(msg, uptimeMillis);
}
複製程式碼

相關概念

  • 一個執行緒對應一個Looper,一個Looper對應一個MessageQueue,一個Looper可對應多個Handler,一個Handler對應一個Looper。
  • 主執行緒中,MessageQueue死迴圈,並不會卡死UI。在ActivityThread的main方法中,首先呼叫Looper.prepareMainLooper,緊接著就會new一個ActivityThread,並且拿到該主執行緒的mainThreadHandler,再呼叫Looper的loop開啟訊息迴圈。以後UI執行緒的UI重新整理等操作也是在mainThreadHandler發訊息執行的。

相關文章