天天看點

Android之HandlerThread源碼分析和簡單使用(主線程和子線程通信、子線程和子線程通信)

3、簡單分析例子

  1)、原始頁面效果

Android之HandlerThread源碼分析和簡單使用(主線程和子線程通信、子線程和子線程通信)

   2)、控制台初始化列印的線程ID

Android之HandlerThread源碼分析和簡單使用(主線程和子線程通信、子線程和子線程通信)

      分析:在onCreate方法裡面列印的主線程的Id為1,有3個按鈕,分别是主線程向子線程發消息,子線程向主線程發消息,子線程和子線程發送消息,我們一開始就是對HandlerThread程序初始化,其實它就是一個線程,後面會分析,然後執行了start方法,我們把HandlerThread的looper對象傳遞給了Handler,之前的文章已分析,一個線程隻能有一個looper對象,Handler擁有了HandlerThread的looper對象,就相當于這個Handler在HandlerThread線程同樣的線程Id,可以了解為Handler 在子線程裡面建構,為什麼我這裡還有其它的Handler建構,因為想搞清楚在哪裡建構屬于哪個線程以及子線程的handler是否可以更新UI,主線程建構的handler是否可以更新UI,

   3)、依次點選3個按鈕後控制台列印的日志

Android之HandlerThread源碼分析和簡單使用(主線程和子線程通信、子線程和子線程通信)

    我們可以看到在onCreate方法裡面始化handler的時候傳遞了一個HandlerThread的looper對象,點選第一個按鈕後後沒有開啟線程,目前線程依然是主線程,handler發送了一個消息,然後初始化的handlMessage收到消息了,也就完成了主線程到子線程的通信,當收到消息的時候,我們發現線程的id是6314,是以這個時候雖然是在onCreate裡面建構的handler裡面的handlerMessage方法,但是線程Id是和HandlerThread線程Id是一樣的,然後初始化handler去更新界面,我們代碼是用mHandlerCtopP去更新的,因為它的初始化是在主線程建構的,是以可以post可以更新UI,但是這個時候用擁有HandlerThread的looper對象的handler更新界面就會出問題,和子線程裡面的handler去更新界面異常一樣,如下圖

Android之HandlerThread源碼分析和簡單使用(主線程和子線程通信、子線程和子線程通信)

然後點選第二個按鈕,是現實子線程向主線程通信,我們發現點選時間裡面的線程Id,和handler收到消息的 handleMessage方法裡面的線程id, 都是一樣,和主線程Id一樣,是以我們可以用這個handler直接post來更新UI,

點選第三個按鈕,是實作子線程和子線程的通信,執行點選方法,我們開啟了一個線程,自然線程Id會和主線程的不一樣,為6318, handler收到消息的 handleMessage方法裡面的線程id為6314,是以這裡可以了解為為子線程線程裡面建構了handler,然後用主線程建構的handler更新ui

  4)、依次點選3個按鈕後手機效果

Android之HandlerThread源碼分析和簡單使用(主線程和子線程通信、子線程和子線程通信)

4、HandlerThread.java源碼分析

   1、上源代碼

package android.os;
 
/**
 * 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.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
 
    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;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }
 
    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // 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;
    }
 
 
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
 
 
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
 
    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}      

1、我們知道這個類在android.os目錄下,然後繼承了Thread,也就是一個線程

2、構造方法會傳一個字元串,這裡可以随便寫,隻作為一個辨別而已,初始化的時候會調用start方法,然後會執行run()方法

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

先執行了Looper,prepare(),然後給目前mLooper指派,然後進行Looper.loop(),進行輪尋,這個代碼和标準的子線程用handler的方式差不多,也可以了解為這種方式的封裝,是以才能顯示子線程和子線程和主線程之間的通信,原理都是一樣。

3、getLooper()中有個wait(),這有什麼用呢?因為的mLooper在一個線程中執行建立,而我們的handler是在UI線程中調用getLooper()初始化的,必須等到mLooper建立完成,才能正确的傳回。getLooper();wait(),notify()就是為了解決這兩個線程的同步問題。

4、這裡有個退出的方法是

public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }      

我之前寫代碼的時候,以為停止線程就調用了,mHandlerThread.stop()方法,後面沒發現什麼問題,直到我們有個功能需要,從控制台下發消息讓手機恢複預設出場,函數執行到了這裡,導緻程序崩潰了,然後就發現這裡有問題,需要調用mHandlerThread.quit()方法正常退出。

5、總結

     有時還需要頻繁更新UI,或則主線程向子線程通信,以及子線程和子線程經常通信的時候,我們可以使用HandlerThread,如果哪裡沒說清楚,或則講得有問題,歡迎點評

繼續閱讀