Android的Handler消息機制 解析

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發消息執行的。
相關文章
相關標籤/搜索