Handler系列文章:java
HandlerThread是什麼?HandlerThread的官方定義是:用於啓動具備looper的新線程的方便類。而後可使用looper來建立Handler類。bash
簡而言之就是:HandlerThread是一個啓動好Looper的Thread對象,方便將Handler綁定在該線程中。ide
//建立子線程Handler
HandlerThread mHandlerThread = new HandlerThread("daqi");
mHandlerThread.start();
Handler mHandler = new Handler(mHandlerThread.getLooper());
mHandler.post(new Runnable() {
@Override
public void run() {
}
});
複製代碼
HandlerThread實際繼承自Thread,實際也是一個Thread對象。oop
其中存在3個比較重要的變量:
一、mPriority 線程優先級
二、mLooper 本線程的Looper
三、mHandler 自帶的Handler變量
源碼分析
#HandlerThread.java
public class HandlerThread extends Thread {
int mTid = -1;
//線程優先級
int mPriority;
//HandlerThread的Looper對象
Looper mLooper;
//自帶的Handler
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
//使用默認優先級
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
}
複製代碼
當不指定線程優先級建立HandlerThread時,使用默認優先級初始化線程。post
Thread對象最關鍵的就是run(),先從run方法看起:ui
HandlerThread.java
@Override
public void run() {
mTid = Process.myTid();
//初始化Looper對象
Looper.prepare();
//加鎖
synchronized (this) {
//獲取該Thread的Looper對象
mLooper = Looper.myLooper();
//getLooper()再解析
notifyAll();
}
//設置該線程的優先級
Process.setThreadPriority(mPriority);
//Looper初始化完畢回調方法,用戶可在這裏初始化Handler
onLooperPrepared();
//啓動Looper循環器
Looper.loop();
mTid = -1;
}
複製代碼
先初始化本線程的Looper對象,並賦值到mLooper對象上。
this
再回調onLooperPrepared(),用戶可重寫該方法,在該方法中進行初始化Handler的操做。
spa
最後才調用Looper#loop()啓動Looper的循環獲取Message機制。
線程
總得來講HandlerThread#run()中進行了Looper初始化和Looper初始化完畢回調。
常用HandlerThread#getLooper()在初始化Handler時指定Handler所在的線程。查看getLooper()源碼:
public Looper getLooper() {
//isAlive()檢測線程是否還存活
if (!isAlive()) {
return null;
}
//加鎖
synchronized (this) {
//當mLooper不爲空時,纔會返回mLooper
while (isAlive() && mLooper == null) {
try {
//等待
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
複製代碼
當Looper沒被初始化完畢時,調用getLooper()的線程會被掛起,等待Looper初始化完畢調用notifyAll()將其喚醒。
當HandlerThread再也不使用時,須要調用quit()或quitSafely()退出Looper的循環機制。
由於run方法最後調用Looper#loop(),啓動循環獲取Message機制,run方法被"卡"在Looper#loop()上。只要當Looper退出循環機制時,run方法才執行完畢,線程也就「完成任務」自我銷燬。
那 quit() 或 quitSafely() 有什麼區別呢?
#HandlerThread.java
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.java
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
複製代碼
區別就是調用MessageQueue#quit()方法時,傳遞的boolean值參數不同。繼續查看MessageQueue#quit()的源碼:
#MessageQueue.java
void quit(boolean safe) {
//...
synchronized (this) {
if (mQuitting) {
return;
}
//標記正在退出
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
複製代碼
當調用HandlerThread#quitSafely()時,最終調用MessageQueue#removeAllFutureMessagesLocked()
當調用HandlerThread#quit()時,最終調用MessageQueue#removeAllMessagesLocked()
private void removeAllMessagesLocked() {
//獲取鏈表表頭
Message p = mMessages;
//直接將Message鏈表所有回收
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
//將鏈表頭置空
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
//獲取當前手機開機時間
final long now = SystemClock.uptimeMillis();
//獲取MessageQueue中的Message鏈表表頭
Message p = mMessages;
if (p != null) {
//若是鏈表表頭Message對象的‘執行時間’比如今小,即代表當前暫無須要執行的Message
if (p.when > now) {
//即走quit()的路線,所有清除鏈表中的Message
removeAllMessagesLocked();
} else {
//不然當前存在須要立刻執行 但又未發送到目標Handler進行處理的Message。
Message n;
for (;;) {
n = p.next;
//找到鏈尾尚未找到比當前時間大,即表示當前Message鏈表中的所有Message都是在退出前發送的,或者說在退出以前須要執行,但如今還沒執行的。
//同時也表示沒有延遲執行的Message,無須要清理的Message,直接退出方法。
if (n == null) {
return;
}
//當找到第一個不是立刻執行的Message對象時,退出死循環。
if (n.when > now) {
break;
}
p = n;
}
//此時for循環退出,說明找到比當前開機時間大,須要延遲執行的Message。
//p.when仍比now(即當前開機時間)小,p.next.when纔是比now大的。
//將p.next置空,即p做爲當前鏈表的鏈尾,後面延遲的Message再也不在鏈表中。
p.next = null;
//死循環,直到到鏈尾
//不斷回收when比當前開機時間大的Message。
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
複製代碼
從這兩個源碼中看出:
HandlerThread#quit()會所有清空MessageQueue中Message鏈表。
HandlerThread#quitSafely()先獲取當前手機開機時間 now,只清除when大於now的Message,即被延遲處理的Message。
而對於小於當前手機開機時間的Message,則進行保留。而那些Message本該在HandlerThread退出前被處理的,但被正在處理的Message「卡住」,還沒來得及處理,被迫留在Message鏈表中。
HandlerThread中還有一個不多被使用的方法,返回一個綁定當前線程的Handler實例:
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
複製代碼
爲何HandlerThread要和IntentService一塊兒講?由於IntentService其內部使用的就是HandlerThread,一個活生生的HandlerThread」實戰案例「。加之有剛纔HandlerThread的源碼基礎,理解IntentService源碼也事半功倍。
什麼是IntentService?簡單點說就是:自帶工做線程的Service。
IntentService是抽象類,須要繼承實現onHandleIntent()方法。在方法中處理啓動Service時傳遞的Intent對象。 IntentService也是Service的子類,聽從Service的生命週期。先從onCreate()方法看起:
#IntentService.java
public abstract class IntentService extends Service {
//工做線程Looper
private volatile Looper mServiceLooper;
//綁定工做線程的Handler
private volatile ServiceHandler mServiceHandler;
//HandlerThread的名稱
private String mName;
//
private boolean mRedelivery;
//定義一個內部Handler類
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//將Message中存儲的Intent對象,回調onHandleIntent(),傳遞給用戶處理。
onHandleIntent((Intent)msg.obj);
//嘗試自我銷燬
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
@Override
public void onCreate() {
super.onCreate();
//建立HandelrThread
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//存儲工做線程的Looper
mServiceLooper = thread.getLooper();
//建立handler並將其綁定在工做線程中。
mServiceHandler = new ServiceHandler(mServiceLooper);
}
}
複製代碼
初始化IntentService時,會建立IntentService工做的子線程,將初始化其內部Handler,綁定在工做線程中。
#IntentService.java
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
複製代碼
啓動service時,只有第一次初始化時纔會調用onCreate(),onStartCommand()和onStart()每次都被調用。onStartCommand會告訴系統如何重啓服務,如判斷是否異常終止後從新啓動等。
onStartCommand()中會調用onStart()。IntentService#onStart(Intent,int)中將Intent對象封裝成Message對象,交由Handler進行發送。
而內部的Handler對象在handleMessage()中,將Intent對象從Message中取出,回調onHandleIntent(),交由用戶進行處理。
最後onStartCommand的返回存在兩種狀況:
一、START_REDELIVER_INTENT:若是此Service的進程在啓動時被終止(即返回onStartCommand(Intent,int,int)以後),則Service將會被安排從新啓動,而且最後一次傳遞的Intent將再次經過onStartCommand(Intent,int,int)從新傳遞給Service。
二、START_NOT_STICKY:若是在執行完onStartCommand後,服務被異常終止,系統不會自動重啓該服務。
mRedelivery用來標識是否將最後一次的Intent從新傳遞一遍。能夠經過setIntentRedelivery進行設置:
#IntentService.java
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
複製代碼
通常狀況下,mRedelivery爲默認值,即爲false。因此onStartCommand()通常返回START_NOT_STICKY,即被殺死後,則不從新啓動再傳遞一次最後的Intent對象。
@Override
public void onDestroy() {
mServiceLooper.quit();
}
複製代碼
IntentService被銷燬時,也會退出Looper,結束工做線程。