併發編程(6)HandlerThread源碼分析

概述

前面分析了不少併發編程方面的東西,可是都是Java層面的,其實Google原生也提供了一些類方便咱們進行併發編程,比較常見的有HandlerThreadIntentServiceAsyncTask,除此以外還有一些第三方框架VolleyPicasso等。java

研究這些類以及開源框架的實現,可讓咱們更好地理解併發編程,甚至是本身也能夠寫一個異步框架也不是什麼難事,下面就來從源碼的角度按照順序來先分析一下HandlerThread編程

正文

註釋

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。注意start()方法必須首先被調用。bash

看過Handler的源碼都應該比較熟悉,Handler的消息是須要Looper來進行輪詢的,也就是每一個Handler建立的時候都須要傳入一個Looper,不過咱們平時建立Handler的時候之因此不須要傳入Looper,是由於主線程默認爲咱們建立了一個looper,固然咱們也能夠傳入本身的Looper.因此爲了不每次在子線程中建立Handler都須要建立Looper,Google爲咱們提供了HandlerThread這個類。併發

成員變量

int mPriority;//線程優先級
    int mTid = -1;//線程ID
    Looper mLooper;//建立線程的Looper
複製代碼

繼承關係

HandlerThread

繼承關係比較簡單,僅僅繼承自Thread,在內部作了一些封裝。框架

構造方法

public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
複製代碼

這個方法Google都沒有註釋,太簡單了,就是傳入一個線程名稱,而後優先級是默認的優先級0異步

public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
複製代碼

構造方法中新加了一個線程優先級ide

run方法

@Override
    public void run() {
        //獲取進程ID
        mTid = Process.myTid();
		//Loopr準備
        Looper.prepare();
        //建立Looper
        synchronized (this) {
            mLooper = Looper.myLooper();
          //喚醒全部等待的線程
            notifyAll();
        }
        //設置線程優先級
        Process.setThreadPriority(mPriority);
      	//在Looper循環時作一些準備工做
        onLooperPrepared();
        //開啓循環
        Looper.loop();
        mTid = -1;
    }
複製代碼

getLooper

獲取子線程的Looperoop

public Looper getLooper() {
       //若是線程已經消亡,就返回null
        if (!isAlive()) {
            return null;
        }
        //若是線程已經建立了,就在此處停留等待Looper建立完成以後
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                  //等待線程被建立
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
複製代碼

quit方法

public boolean quit() //獲取looper Looper looper = getLooper();
		//退出looper
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
複製代碼

quitSafely

public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
複製代碼

跟quit方法的惟一區別在於looper.quit()變成了looper.quitSafely(),如今具體分析一下這兩個方法的區別post

public void quit() {
        mQueue.quit(false);
    }
    
    public void quitSafely() {
        mQueue.quit(true);
    }
複製代碼

一個傳入了false,一個傳入了true,繼續追蹤ui

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
              //移除消息隊列中延遲的消息
                removeAllFutureMessagesLocked();
            } else {
              //移除消息隊列中全部的消息
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
複製代碼

removeAllFutureMessagesLocked方法

private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
              	//當消息隊列中中的消息的發送時間大於當前時間
        		//就移除該消息
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
複製代碼

因此看到這裏,quit跟quidSafely的區別就在因而否移除消息隊列中還未發送也就是延遲的消息。

使用

//建立mHandlerThread
        mHandlerThread = new HandlerThread("main");
        //獲取HandlerThead中的Looper
        Looper looper = mHandlerThread.getLooper();
        //建立子線程中的Looper
        Handler handler = new Handler(looper);
        //執行耗時操做
        handler.post(new Runnable() {
            @Override
            public void run() {
                //子線程中執行耗時操做
            }
        });
        
        
   //界面銷燬的時候須要銷燬Looper
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandlerThread.quit();
    }
複製代碼

總結

若是沒有HandlerThread,咱們須要手動去建立一個線程,如今HandlerThread能夠幫咱們簡化這個操做,可是有一點須要注意的是,因爲咱們的異步操做是存放在Handler的消息隊列中的,因此是串行的,因此只適合併發量較少的耗時操做。

相關文章
相關標籤/搜索