Android進階知識:HandlerThread相關

1.前言

前一篇文章中瞭解了AsyncTask的使用和原理,這一篇輪到HandlerThread這種異步任務的方式,HandlerThread源碼中會涉及一些關於HandlerLooper的內容,不太瞭解的能夠先看一下這篇文章Android進階知識:Handler相關java

2.HandlerThread使用

HandlerThread的使用有如下幾個步驟:android

1. 建立HandlerThread對象bash

HandlerThread handlerThread = new HandlerThread("myHandlerThread");
複製代碼

2.開啓HandlerThread異步

handlerThread.start();
複製代碼

3.建立HandlerThread的Handler,並複寫其handleMessage方法ide

Handler handler = new Handler(handlerThread.getLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case type1: {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.d(TAG, Thread.currentThread().getName() + " type1 receive");
                }
                break;
                case type2: {
                    for (percent = 0; percent <= 100; percent += 10) {
                        try {
                            Thread.sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Log.d(TAG, Thread.currentThread().getName() + " type2 " + percent + "% progress");

                    }
                    Log.d(TAG, Thread.currentThread().getName() + ":finish");
                }
                break;
            }
        }
    };        
複製代碼

4.經過Handler發送消息函數

Message message1 = Message.obtain();
message1.what = type1;
handler.sendMessage(message1);

Message message2 = Message.obtain();
message2.what = type2;
handler.sendMessage(message2);
複製代碼

5.使用完退出HandlerThreadoop

handlerThread.quit();
複製代碼

運行結果日誌:post

3.HandlerThread運行原理

不管從HandlerThread的名字仍是它的使用方法來看HandlerThread都是一個線程Thread加上一個Handler的組合,其實也確實如此。先從它的構造方法開始看。ui

/**
 * 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;
    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;
    }
    ......
    }
複製代碼

首先看到它確實是繼承了Thread類,從HandlerThread類上的註釋能夠看出HandlerThread就是一個提供帶有Looper的線程便利類,能夠經過這個Looper建立Handler。咱們知道子線程中使用Handler須要本身手動建立Lopper,而使用HandlerThread就不須要了,它裏面已經幫咱們建立好了LooperHandlerThread它有兩個重載的構造方法,構造方法中只作了兩件事,就是這個線程設置名字和優先級。做爲一個Thread類想要使用它就必須調用start方法開啓線程,開啓線程後就會執行它的run方法,繼續來看它的run方法實現。this

@Override
    public void run() {
        // 獲取線程id
        mTid = Process.myTid();
        // 建立Looper
        Looper.prepare();
        synchronized (this) {
            // 獲取當前線程的Looper
            mLooper = Looper.myLooper();
            // 通知等待喚醒
            notifyAll();
        }
        // 設置線程優先級
        Process.setThreadPriority(mPriority);
        // 開啓Looper循環前的準備方法
        onLooperPrepared();
        // 開啓輪詢
        Looper.loop();
        // 將線程id修改成-1
        mTid = -1;
    }
複製代碼

run方法中首先獲取線程id,而後就調用了Looper.prepare方法建立一個Looper,接着調用了Looper.myLooper方法獲取到了當前線程的Looper。接着經過notifyAll通知等帶喚醒,這裏的等待是在HandlerThreadgetLooper方法裏調用的wait方法,getLooper方法是爲了獲取該HandlerThread中的Looper。若是在沒調用HandlerThreadstart方法開啓線程前就調用getLooper方法就經過wait方法暫時先進入等待,等到run方法運行後再進行喚醒。喚醒以後run方法中繼續設置了構造函數中傳入的優先級,接着調用了onLooperPrepared方法,該方法是個空實現,該方法是爲了在Looper開啓輪詢以前若是要進行某些設置,能夠複寫該方法。最後調用Looper.loop開啓輪詢。

public Looper getLooper() {
        // 若是該線程isAlive爲false直接返回null
        if (!isAlive()) {
            return null;
        }
        // 若是isAlive爲true但mLooper爲null,就進行等待直到mLooper被建立
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
複製代碼

onLooperPrepared空實現。

protected void onLooperPrepared() {
 }
複製代碼

HandlerThread中還提供了兩個退出的方法分別對應Looper中的兩個退出方法。

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;
    }
複製代碼

能夠看到無論哪一個方法其中都先獲取Looper判斷不爲空後調用了Looper中的對應的退出方法。使用完HandlerThread後必定要記得調用退出方法中止Looper,不然Looper會一直輪詢。

除了以上方法,HandlerThread裏還有一個getThreadHandler獲取持有當前線程LooperHanlder,不過這個方法是hide隱藏的。

/**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
複製代碼

HandlerThread中的方法

4.總結

以上就是HandlerThread全部源碼方法,能夠發現HandlerThread源碼很少一共160多行,其實現的功能也不復雜,就是封裝了一個帶有Looper的線程類,方便了咱們作異步耗時任務和通訊。不過使用仍是有幾個要注意的點:

  • 由於HandlerThread只有一個線程,因此在連續不停的使用Handler的發送消息時,任務只能一個一個進行。
  • HandlerThread用完記得調用退出方法。
  • 由於要使用Handler因此會容易發生內存泄漏。
相關文章
相關標籤/搜索