瞭解HandlerThread這一篇就夠了

HandlerThread解析及應用

本文咱們講解的是HandlerThread,可能有部分同窗不瞭解或者沒用過這個類。緣由很簡單,在現在大神們的框架面前,許多的原生類已經不爲人所用了,然而早期開發者要想在子線程裏更新UI則須要在Thread裏建立一個Handler。java

class MyThread : Thread() {
    private lateinit var mHandler: Handler

    override fun run() {
        super.run()
        Looper.prepare()

        mHandler = object : Handler() {
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                if(msg?.what==0){
                    //...處理消息
                }
            }
        }

        Looper.loop()
    }
}
複製代碼

是否是認爲很難受?每次要想在子線程裏面處理信息必須每次在子線程裏建立Handler?

android

然而貼心的Google工程師爲咱們提供了一個自帶Handler的類,名叫 HandlerThread

bash

類註釋

要想學習一個類的構成,首先要從它的類註釋看起。數據結構

**
 * 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 {
    ...
}
複製代碼
  • 由它的註釋能夠看出,這是一個帶有LooperThread,這個Looper能夠被用於建立Handler,請注意,start()這個方法仍然須要被調用。
  • 總結一下,這個類就是自帶Handler的線程類。

構造方法

int mPriority;//優先級
    int mTid = -1;
    Looper mLooper;//自帶的Looper
    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;
    }
複製代碼
  • 這裏有兩個構造方法,一個HandlerThread(String name),一個HandlerThread(String name, int priority),咱們能夠本身設定線程的名字以及優先級。注意!是Process裏的優先級而不是Thread的。
//這是可選的優先級
    public static final int THREAD_PRIORITY_DEFAULT = 0;
    public static final int THREAD_PRIORITY_LOWEST = 19;
    public static final int THREAD_PRIORITY_BACKGROUND = 10;
    public static final int THREAD_PRIORITY_FOREGROUND = -2;
    public static final int THREAD_PRIORITY_DISPLAY = -4;
    public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
    public static final int THREAD_PRIORITY_VIDEO = -10;
    public static final int THREAD_PRIORITY_AUDIO = -16;
    public static final int THREAD_PRIORITY_URGENT_AUDIO = -19;
    public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1;
    public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;
複製代碼

其餘方法

/*
        在Looper.loop()以前調用的方法,如須要配置可重寫
    */
    protected void onLooperPrepared() {
    }


    /*
        來自Thread的run方法,調用與start()以後    
    */
    @Override
    public void run() {
        mTid = Process.myTid();//線程id
        Looper.prepare();//建立子線程的Looper
        synchronized (this) {
            mLooper = Looper.myLooper();//獲取Looper實例
            notifyAll();// native方法,用於喚醒全部等待獲取Looper的線程
        }
        Process.setThreadPriority(mPriority);//設置線程優先級
        onLooperPrepared();//上面方法的調用
        Looper.loop();//Looper開始輪詢
        mTid = -1;
    }
    
    /*
        獲取Looper
    */
    public Looper getLooper() {
        //判斷線程是否啓動
        if (!isAlive()) {
            return null;
        }
        
        synchronized (this) {
            while (isAlive() && mLooper == null) {//若是線程啓動,且Looper爲空
                try {
                    wait();//使其等待直至獲取到Looper
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /*
        獲取Handler
    */
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    
    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;
    }

    
    public int getThreadId() {
        return mTid;
    }
複製代碼
  • 註釋我已寫在代碼上,在HandlerThread不使用時記得使用 getLooper().quit()來退出這個線程,但須要注意的是quit和quitSafely的區別,接下來我將用較短的篇幅介紹一下這兩個方法的區別。

quit()和quitSafely()的區別

HandlerThread:
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在這兩個方法中調用了quit()quitSafely()方法。咱們點開源碼查看,他們本質都是在調用MessageQueuequit方法,不一樣的是quit方法的參數不一樣:框架

Looper:

quitSafely:ide

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

quit:oop

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

若是你們對消息機制有所瞭解,那麼必定知道MessageQueue是用於處理Message(消息)的隊列,若是不知道的能夠去了解一下。話很少說,接下來咱們將深刻MessageQueue瞭解一下quit(boolean safe)方法是何方神聖。post

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

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

            if (safe) {//敲黑板!這就是quitSafely和quit的區別
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
複製代碼
  • 當咱們在Looper中調用quitSafely()時,在MessageQueue裏調用的removeAllFutureMessagesLocked()方法
  • 當咱們在Looper中調用quit()時,在MessageQueue裏調用的是removeAllMessagesLocked()方法
  • 而這兩個方法本質的區別就是在於remove消息的範圍,下面是一段僞代碼:
if (safe) {
                只清空隊列中的延時消息(經過postDelay發送的),非延時消息繼續派發,直到完成
            } else {
                移除全部消息,包括延時消息
            }
複製代碼

因此說safe的區別就是是否清除全部消息,這兩個方法的實現咱們就不深究了,畢竟本文是爲了講解HandlerThread而不是數據結構,如有興趣的能夠去自行研究。學習

應用

  • IntentService的底層就是利用HandlerThread
  • 舉個栗子

    Activity:
class MainActivity : AppCompatActivity() ,Handler.Callback{


    private lateinit var mUIHandler :Handler
    private lateinit var mDownloadThread: DownloadThread

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        init()
    }

    private fun init(){
        mUIHandler = Handler(this)

        val list = mutableListOf<String>()
        list.add("url1")
        list.add("url2")
        list.add("url3")

        mDownloadThread = DownloadThread("Download")
                        .setUrls(list)
                        .setUIHandler(mUIHandler)

        mDownloadThread.start()
        Log.d("test","開始下載")
    }

    override fun handleMessage(msg: Message?): Boolean {
        when(msg?.what){
            DownloadThread.START->{
                Log.d("test","Activity接收到了START信息")
            }
            DownloadThread.FINISH->{
                Log.d("test","Activity接收到了FINISH信息")
            }
        }
        return true
    }
}
複製代碼


DownloadThread:ui

class DownloadThread(name: String?) : HandlerThread(name), Handler.Callback {
    private lateinit var mWorkHandler: Handler
    private lateinit var mUIHandler: Handler
    private lateinit var urls: List<String>

    companion object {
        const val START = 1
        const val FINISH = 2
        const val KEY = "getUrl"
    }

    fun setUIHandler(mUIHandler: Handler): DownloadThread {
        this.mUIHandler = mUIHandler
        return this
    }

    fun setUrls(urls: List<String>): DownloadThread {
        this.urls = urls
        return this
    }

    override fun onLooperPrepared() {
        super.onLooperPrepared()
        if (looper != null)
            mWorkHandler = Handler(looper, this)

        //在這裏發送下載信息
        urls.forEach { url->
            val message = mWorkHandler.obtainMessage()
            val bundle = Bundle()
            bundle.putString(KEY,url)
            message.data = bundle
            mWorkHandler.sendMessage(message)
        }
    }


    //mWorkHandler的handleMessage
    override fun handleMessage(msg: Message?): Boolean {
        if (msg == null || msg.data == null) return false

        val url = msg.data.get(KEY) as String//獲取url

        val startMessage: Message = mUIHandler.obtainMessage(START)
        mUIHandler.sendMessage(startMessage)
        Log.d("test","$url :Thread發送START信息")

        Thread.sleep(2000)
        Log.d("test","$url :Thread執行耗時操做中...")

        val finishMessage: Message = mUIHandler.obtainMessage(FINISH)
        mUIHandler.sendMessage(finishMessage)
        Log.d("test","$url :Thread發送FINISH信息")
        return true
    }
}
複製代碼

結果:

2019-01-30 20:47:47.743  D/test: 開始下載

2019-01-30 20:47:47.744  D/test: url1 :Thread發送START信息
2019-01-30 20:47:47.776  D/test: Activity接收到了START信息
2019-01-30 20:47:49.746  D/test: url1 :Thread執行耗時操做中...
2019-01-30 20:47:49.747  D/test: url1 :Thread發送FINISH信息
2019-01-30 20:47:49.747  D/test: Activity接收到了FINISH信息

2019-01-30 20:47:49.747  D/test: url2 :Thread發送START信息
2019-01-30 20:47:49.747  D/test: Activity接收到了START信息
2019-01-30 20:47:51.748  D/test: url2 :Thread執行耗時操做中...
2019-01-30 20:47:51.749  D/test: url2 :Thread發送FINISH信息
2019-01-30 20:47:51.750  D/test: Activity接收到了FINISH信息

2019-01-30 20:47:51.749  D/test: url3 :Thread發送START信息
2019-01-30 20:47:51.750  D/test: Activity接收到了START信息
2019-01-30 20:47:53.750  D/test: url3 :Thread執行耗時操做中...
2019-01-30 20:47:53.751  D/test: url3 :Thread發送FINISH信息
2019-01-30 20:47:53.751  D/test: Activity接收到了FINISH信息
複製代碼
總結

上面就是利用HandlerThread在子線程中執行串行任務,並反饋到主線程的栗子。總的來講HandlerThread的知識點就是這些,如有錯誤或者遺漏歡迎指出。

相關文章
相關標籤/搜索