Android_HandlerThread 源碼梳理

前言

Android 多線程還有HandleThread,看名字就可以能感覺到得到,會是handler和Thread的綜合使用。那到底是怎麼樣的呢,現在就跟隨Android的源碼來看看他的工作原理是什麼樣的。

我們先看看他的類註解:先看看官方對他的介紹:【 Handy class for starting a new thread that has a looper. The looper can then be  used to create handler classes. Note that start() must still be called.】簡單的翻譯就是這是一個很方便啓動一個擁有looper的的類,這個Looper在這以後是用來創建handler類的,注意:thread的start()方法一定要被調用。

理解HandleThread

看到介紹可能覺得HandleThread有點厲害,有looper還能創建handler。我們抱着這樣的心情去看HandleThread比較難受,所以,我們要換一下思路,這個HandleThread就是個Thread,這個Thread執行工作任務,我們通過handler給這個Thread安排工作。【從使用角度就相當於我們自己起一個工作線程,然後在線程中自己prepareLooper一樣。只不過HandleThread已經把這些做好了,還封裝了優先級設置,安全退出等一些輔助功能,讓我們開發人員使用起來更加方便

handler我們知道可以用來線程間通信,之前在Handler工作流程梳理裏面分析過【具體在本篇就不細說】,我們這裏要知道,Handler可以把Message發送到MessageQueue中,通過Looper.loop()循環將Message取出並分派到到相應的Handler去處理。同一個線程中只有一個MessageQueue和Looper, Message的處理是串行的。當有Thread使用了Handler,那麼通過Handler給Thread安排任務也是串行執行的,就是一個執行完才執行下一個,所以這個HandleThread不適多耗時任務,這樣任務的執行相互收影響比較嚴重。對於任務量小,使用頻繁的任務來說就比較友好,可以使用一個線程來實現線程池的效果,節省了資源。

源碼分析

看了上面的理解描述後,我們就可以猜到HandleThread代碼邏輯應該不復雜,代碼量也不會很大。事實就是這樣,加上註解,HandleThread的代碼在160多行。

/frameworks/base/core/java/android/os/HandlerThread.java

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    ...
}

HandleThread繼承自Thread,首先我們就看到了該有的Looper對象,然後從它的構造函數看,可以設置線程優先級和給線程命名。在使用時如果不設置優先級,會默認設置爲Process.THREAD_PRIORITY_DEFAULT;

HandleThread主要的邏輯就在run()方法裏面

1.run()

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

在run()裏。Looper.prepare()來初始化當前線程的一個Looper。然後拿到一個looper對象 賦值給mLooper,呃呃呃。這裏爲啥要notifyAll(),在Java中wait()和notify/notifyAll是一起使用的,主要用在多線程中,,用在這裏又是爲什麼?我們在HandleThread搜索一下wait()果然有:

1.2 getLooper()

public Looper getLooper() {
    ...
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

在getLooper()方法裏,當looper還沒有創建時間,需要當前線程保持wait,這是爲什麼呢?我估計是當線程啓動,在沒有獲取到looper之前,當前線程是空閒的,也沒任務做,就先釋放資源,讓其他線程先執行run()方法來獲取Looper對象,然後將looper賦值給mLooper,【當我看到這裏時是很疑惑的,這個在文章後面來介紹】;

再回到run()方法中;獲mLooper賦值後,隨後調用notifyAll(),【此時其他線程的getLooper()方法也會被喚醒,就可以利用looper去創建handler。然後再handleMessage()中實現處理任務的業務代碼】;然後接下里就主要是進入loop循環了。這就之前分析handler看到的線程最後的狀態是一毛一樣,那就是隨後線程都會進入到loop循環中。

以上就是HandleThread的主要原理了。

----------------------------------------------------------華麗麗的分割線--------------------------------------------------------------------------

疑惑

現在要來說說我在分析那個mlooper被賦值時遇到的疑惑,還記得我們在Handler工作流程梳理事看到Looper.getLooper()最後是從ThreadLoacl中取出的,在Android_ThreadLoacl原理一篇中,我們知道threadLocal保存的變量是與線程相關聯的,所以不同線程get到的Looper是不一樣的。

疑點來了啊,注意聽。假如線程A運行到HandleThread的getLooper()方法,如果此時mlooper爲空。那麼線程A就會wait,阻塞在這裏;那麼後來線程B運行到HandleThread的run()方法,從線程B中獲取到線程B的looper,然後賦值給mLooper;之後線程A就可以被喚醒,返回mLooper;那麼這時候線程A返回的looper其實是在線程B中創建的looper,我就很亂了,,,不應該looper和線程相關的嗎?在這裏怎麼可以這麼玩?

反正我是很亂的,問了同事也還沒問個明白。有一點肯定的,源碼中這麼玩肯定是沒錯的,我需要一個可以讓自己信服的理由。

既然沒問道直接的答案,按我自己試試,然後,我寫了個demo,在主線程中獲取主線程的MainLooper,然後在子線程創建Handler時直接傳入MainLooper。最後發現, 子線程是可以使用主線程的looper。恩恩,終於,用事實說明了,多線是可以公用looper的。好了,HandleThread中的疑惑解決了;

接下來,我就有了新疑惑,我需要繼續搞明白,爲什麼多線程可以共用looper。這和我之前的理解不一樣,我之前的理解是線程中的looper和線程相關,當前線程只能使用當前線程的Looper。

解惑

答案還是得要在源碼裏面找,經過觀察發現HandleThread和我們一般的在子線程中創建Handler的方式是不同的;

  • 子線程中直接我們一般是這樣的:
/ * 
  * <pre>
  *  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();
  *      }
  *  }</pre>
  * /
  • 使用HandelrThread時是這樣的
private HandlerThread mMyhandleThread = new HandlerThread("MyHandleThread");
Handler mHandler;
mMyhandleThread.start();
mHandler = new Handler(mMyhandleThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
            // TODO
            }
};

我感覺問題還是在Handler上。那我們就去看看Handler 的構造函數:

/frameworks/base/core/java/android/os/Handler.java

// 1.我們一般手動在子線程就是這樣創建Handler的
public Handler() {
    this(null, false);
}
...
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());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
//------------------------華麗麗的分割線------------------------

// 2.我們一般使用HandlerThread時是這樣創建Handler的
public Handler(Looper looper) {
    this(looper, null, false);
}
...
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

通過對不同方式創建Handler時調用的構造方法,我們發現 ,handler的如果在構造函數中指定了Looper,就直接使用傳進來的looper對象,如果在構造事沒有傳入looper對象,則此時就需要使用本線程自己的looper;至於messageQueue只是和Looper相關,MessageQueue和handler並沒有直接聯繫。

結論

所以我們得出結論,多線程可以使用同一個Looper,相應的也就是使用了相同的MessageQueue。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章