天天看點

子線程和主線程建立使用Handler不同處的源碼分析

子線程的Handler

在使用handler時,會有在子線程建立handler的場景,那我們從Looper.java的源碼中摘抄下面一段建立Hander的代碼段:

class LooperThread extends Thread {
     public Handler mHandler;
     public void run() {
         Looper.prepare();
         mHandler = new Handler() {
             public void handleMessage(Message msg) {
                 // process incoming messages here
             }
         };
         Looper.loop();
     }
 }
           

上面是一般使用在子線程建立和使用Handler的代碼段,我們對比以前在主線程建立和使用Handler的代碼,可以發現基本差不多,但是有一點不一樣,那就是在子線程中多了下面這兩行代碼:

Looper.prepare();
Looper.loop();
           

1.1 prepare

就是子線程需要prepare,可以直接了解為組要準備目前線程的looper。就是說子線程沒有looper,那我們看看我們怎麼準備looper的?

/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
    prepare(true);
}
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));
}
           

源碼的注釋也說得很清楚,就是建立handler需要先有looper。prepare()方法裡面是調用了prepare(boolean quitAllowed)方法,後面這個方法比前面就多了個參數,這個參數【quitAllowed】看字面意思可以看出作用就是:允不允許looper退出,從代碼中我們可以知道子線程的looper是可以退出的;然後在prepare(boolean quitAllowed)中首先檢查時候已經存在looper了,如果已經含有了looper,那就報錯,這下我們就知道了Android是怎麼保證一個線程隻有一個looper了,如果目前線程還不存在looper,那就直接new一個新的looper,然後将建立的looper塞進sThreadLocal中。

1.2 loop

那麼子線程中loop()方法是什麼呢?這個loop()方法就是在目前線程中運作消息隊列,并切在結束時需要調用quit()方法.我們接着針對loop()看看源碼裡面做了什麼:

/**
    * Run the message queue in this thread. Be sure to call
    * {@link #quit()} to end the loop.
    */
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    for (;;) {
        Message msg = queue.next(); // might block
        ...
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } 
        ....
        msg.recycleUnchecked();
    }
}
           

在loop()方法中主要就是從消息隊列中擷取消息,然後執行資訊分發【這個具體先不深入,我們隻要知道loop()的主要工作就行】,對比一下我們在主線程中使用Handler的情況,在子線程中使用Handler很像是條件不好人要自立更生艱苦奮鬥,沒有條件就自己創造條件。

主線程的Handler

我們看了子線程中如何使用Handle和相關源碼分析,接下來再看看主線程中我們是怎麼使用handler的?根據以前的經驗,主線程建立使用Handler就是直接建立的,并沒有其他的操作:

public Handler mHandler;
mHandler = new Handler() {
    public void handleMessage(Message msg) {
       // process incoming messages here
    }
};
           

相比子線程,使用時少了prepare()和loop();但是真的是主線程不需要調用這些方法嗎?顯然不是的,子線程有的主線程也不能少,那接下來我們看看是誰幫了我們的忙?

還記得之前在Android_Application啟動梳理時,就發現了在ActivityThread的面main函數就有這麼兩行行代碼:

public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    Looper.loop();
}
           

2.1 prepareMainLooper

那我們就看看這個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()方法,但是傳入的參數和在子線程中不同,這裡這傳入的是false,也就是說主線程的looper是不允許退出的;到這裡,我們知道prepareMainLooper将目前線程初始化為循環程式,将其标記為應用程式的主循環程式。應用程式的主循環是由android環境建立的,是以使用者不必自己調用此函數

2.2 loop

在prepareMainLooper()方法後也調用了loop()方法,看到這裡,我們就明白了主線程在使用handler的條件在建立程式之初就已經具備,是以讓使用者在主線程中使用handler也友善很多。

總結

1.也可以在子線程建立handler,需要提前prepare looper和自己調用loop。

2.主線程和子線程的差別:主線程的looper建立完後不可以退出,子線程建立的looper是可以退出的,需要自己去prepare()和loop(),主線程的handler環境在程序啟動的時候就建立好了。

繼續閱讀