本文咱們講解的是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 {
...
}
複製代碼
Looper
的Thread
,這個Looper
能夠被用於建立Handler
,請注意,start()
這個方法仍然須要被調用。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;
}
複製代碼
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()
方法。咱們點開源碼查看,他們本質都是在調用MessageQueue
中quit
方法,不一樣的是quit方法的參數不一樣:框架
quitSafely:ide
public void quit() {
mQueue.quit(true);
}
複製代碼
quit:oop
public void quit() {
mQueue.quit(false);
}
複製代碼
若是你們對消息機制有所瞭解,那麼必定知道MessageQueue
是用於處理Message(消息)的隊列,若是不知道的能夠去了解一下。話很少說,接下來咱們將深刻MessageQueue
瞭解一下quit(boolean safe)
方法是何方神聖。post
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()
方法if (safe) {
只清空隊列中的延時消息(經過postDelay發送的),非延時消息繼續派發,直到完成
} else {
移除全部消息,包括延時消息
}
複製代碼
因此說safe的區別就是是否清除全部消息,這兩個方法的實現咱們就不深究了,畢竟本文是爲了講解HandlerThread而不是數據結構,如有興趣的能夠去自行研究。學習
IntentService
的底層就是利用HandlerThread
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的知識點就是這些,如有錯誤或者遺漏歡迎指出。