前言
-
多線程的應用在Android開發中是非常常見的,常用方法主要有:
- 繼承Thread類
- 實現Runnable接口
- Handler
- AsyncTask
- HandlerThread
- IntentService
今天,我將全面解析多線程中
HandlerThread
的源碼
Carson帶你學多線程系列
基礎匯總
Android多線程:基礎知識匯總
基礎使用
Android多線程:繼承Thread類使用(含實例教程)
Android多線程:實現Runnable接口使用(含實例教程)
復合使用
Android多線程:AsyncTask使用教程(含實例講解)
Android多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學
相關使用
Android異步通信:這是一份全面&詳細的Handler機制學習攻略
Android多線程:手把手教你全面學習神秘的Synchronized關鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
目錄
1. 簡介
更加具體請看文章:
2. 源碼分析
- 本次源碼分析將根據
HandlerThread
的使用步驟講解
若不熟悉,請務必看文章Android多線程:手把手教你使用HandlerThread
-
HandlerThread
的使用步驟有5個:
// 步驟1:創建HandlerThread實例對象
// 傳入參數 = 線程名字,作用 = 標記該線程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步驟2:啟動線程
mHandlerThread.start();
// 步驟3:創建工作線程Handler & 復寫handleMessage()
// 作用:關聯HandlerThread的Looper對象、實現消息處理操作 & 與其他線程進行通信
// 注:消息處理操作(HandlerMessage())的執行線程 = mHandlerThread所創建的工作線程中執行
Handler workHandler = new Handler( mHandlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息處理
return true;
}
});
// 步驟4:使用工作線程Handler向工作線程的消息隊列發送消息
// 在工作線程中,當消息循環時取出對應消息 & 在工作線程執行相關操作
// a. 定義要發送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的標識
msg.obj = "B"; // 消息的存放
// b. 通過Handler發送消息到其綁定的消息隊列
workHandler.sendMessage(msg);
// 步驟5:結束線程,即停止線程的消息循環
mHandlerThread.quit();
- 下面,我將根據上述使用步驟進行源碼分析
步驟1:創建HandlerThread的實例對象
/**
* 具體使用
* 傳入參數 = 線程名字,作用 = 標記該線程
*/
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
/**
* 源碼分析:HandlerThread類的構造方法
*/
public class HandlerThread extends Thread {
// 繼承自Thread類
int mPriority; // 線程優先級
int mTid = -1; // 當前線程id
Looper mLooper; // 當前線程持有的Looper對象
// HandlerThread類有2個構造方法
// 區別在于:設置當前線程的優先級參數,即可自定義設置 or 使用默認優先級
// 方式1. 默認優先級
public HandlerThread(String name) {
// 通過調用父類默認的方法創建線程
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
// 方法2. 自定義設置優先級
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
...
}
總結
-
HandlerThread
類繼承自Thread
類 - 創建
HandlerThread
類對象 = 創建Thread
類對象 + 設置線程優先級 = 新開1個工作線程 + 設置線程優先級
步驟2:啟動線程
/**
* 具體使用
*/
mHandlerThread.start();
/**
* 源碼分析:此處調用的是父類(Thread類)的start(),最終回調HandlerThread的run()
*/
@Override
public void run() {
// 1. 獲得當前線程的id
mTid = Process.myTid();
// 2. 創建1個Looper對象 & MessageQueue對象
Looper.prepare();
// 3. 通過持有鎖機制來獲得當前線程的Looper對象
synchronized (this) {
mLooper = Looper.myLooper();
// 發出通知:當前線程已經創建mLooper對象成功
// 此處主要是通知getLooper()中的wait()
notifyAll();
// 此處使用持有鎖機制 + notifyAll() 是為了保證后面獲得Looper對象前就已創建好Looper對象
}
// 4. 設置當前線程的優先級
Process.setThreadPriority(mPriority);
// 5. 在線程循環前做一些準備工作 ->>分析1
// 該方法實現體是空的,子類可實現 / 不實現該方法
onLooperPrepared();
// 6. 進行消息循環,即不斷從MessageQueue中取消息 & 派發消息
Looper.loop();
mTid = -1;
}
}
/**
* 分析1:onLooperPrepared();
* 說明:該方法實現體是空的,子類可實現 / 不實現該方法
*/
protected void onLooperPrepared() {
}
總結
- 為當前工作線程(即步驟1創建的線程)創建1個
Looper
對象 &MessageQueue
對象 - 通過持有鎖機制來獲得當前線程的
Looper
對象 - 發出通知:當前線程已經創建mLooper對象成功
- 工作線程進行消息循環,即不斷從MessageQueue中取消息 & 派發消息
步驟3:創建工作線程Handler & 復寫handleMessage()
/**
* 具體使用
* 作用:將Handler關聯HandlerThread的Looper對象、實現消息處理操作 & 與其他線程進行通信
* 注:消息處理操作(HandlerMessage())的執行線程 = mHandlerThread所創建的工作線程中執行
*/
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息處理
return true;
}
});
/**
* 源碼分析:handlerThread.getLooper()
* 作用:獲得當前HandlerThread線程中的Looper對象
*/
public Looper getLooper() {
// 若線程不是存活的,則直接返回null
if (!isAlive()) {
return null;
}
// 若當前線程存活,再判斷線程的成員變量mLooper是否為null
// 直到線程創建完Looper對象后才能獲得Looper對象,若Looper對象未創建成功,則阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
// 此處會調用wait方法去等待
wait();
} catch (InterruptedException e) {
}
}
}
// 上述步驟run()使用 持有鎖機制 + notifyAll() 獲得Looper對象后
// 則通知當前線程的wait()結束等待 & 跳出循環
// 最終getLooper()返回的是在run()中創建的mLooper對象
return mLooper;
}
總結
- 在獲得
HandlerThread
工作線程的Looper
對象時存在一個同步的問題:只有當線程創建成功 & 其對應的Looper
對象也創建成功后才能獲得Looper
的值,才能將創建的Handler
與 工作線程的Looper
對象綁定,從而將Handler
綁定工作線程 - 解決方案:即保證同步的解決方案 = 同步鎖、
wait()
和notifyAll()
,即 在run()
中成功創建Looper
對象后,立即調用notifyAll()
通知getLooper()
中的wait()
結束等待 & 返回run()
中成功創建的Looper
對象,使得Handler
與該Looper
對象綁定
步驟4:使用工作線程Handler
向工作線程的消息隊列發送消息
/**
* 具體使用
* 作用:在工作線程中,當消息循環時取出對應消息 & 在工作線程執行相關操作
* 注:消息處理操作(HandlerMessage())的執行線程 = mHandlerThread所創建的工作線程中執行
*/
// a. 定義要發送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的標識
msg.obj = "B"; // 消息的存放
// b. 通過Handler發送消息到其綁定的消息隊列
workHandler.sendMessage(msg);
/**
* 源碼分析:workHandler.sendMessage(msg)
* 此處的源碼即Handler的源碼,故不作過多描述
*/
步驟5:結束線程,即停止線程的消息循環
/**
* 具體使用
*/
mHandlerThread.quit();
/**
* 源碼分析:mHandlerThread.quit()
* 說明:
* a. 該方法屬于HandlerThread類
* b. HandlerThread有2種讓當前線程退出消息循環的方法:quit() 、quitSafely()
*/
// 方式1:quit()
// 特點:效率高,但線程不安全
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
// 方式2:quitSafely()
// 特點:效率低,但線程安全
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
// 注:上述2個方法最終都會調用MessageQueue.quit(boolean safe)->>分析1
/**
* 分析1:MessageQueue.quit(boolean safe)
*/
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(); // 方式1(安全)會調用該方法 ->>分析3
} else {
removeAllMessagesLocked(); // 方式2(不安全)會調用該方法 ->>分析2
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
/**
* 分析2:removeAllMessagesLocked()
* 原理:遍歷Message鏈表、移除所有信息的回調 & 重置為null
*/
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
/**
* 分析3:removeAllFutureMessagesLocked()
* 原理:先判斷當前消息隊列是否正在處理消息
* a. 若不是,則類似分析2移除消息
* b. 若是,則等待該消息處理處理完畢再使用分析2中的方式移除消息退出循環
* 結論:退出方法安全與否(quitSafe() 或 quit()),在于該方法移除消息、退出循環時是否在意當前隊列是否正在處理消息
*/
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
// 判斷當前消息隊列是否正在處理消息
// a. 若不是,則直接移除所有回調
if (p.when > now) {
removeAllMessagesLocked();
} else {
// b. 若是正在處理,則等待該消息處理處理完畢再退出該循環
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);
}
}
}
至此,關于HandlerThread
源碼的分析完畢。
3. 總結
-
本文全面分析了多線程中
HandlerThread
的源碼,總結如下
示意圖 下一篇文章我將對講解
Android
多線程的相關知識,感興趣的同學可以繼續關注Carson_Ho的簡書
Carson帶你學多線程系列
基礎匯總
Android多線程:基礎知識匯總
基礎使用
Android多線程:繼承Thread類使用(含實例教程)
Android多線程:實現Runnable接口使用(含實例教程)
復合使用
Android多線程:AsyncTask使用教程(含實例講解)
Android多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學
相關使用
Android異步通信:這是一份全面&詳細的Handler機制學習攻略
Android多線程:手把手教你全面學習神秘的Synchronized關鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
歡迎關注Carson_Ho的簡書
不定期分享關于安卓開發的干貨,追求短、平、快,但卻不缺深度。