HandlerThread與IntentService源碼剖析

Handler系列文章:java

        
        HandlerThread是什麼?HandlerThread的官方定義是:用於啓動具備looper的新線程的方便類。而後可使用looper來建立Handler類。bash

        簡而言之就是:HandlerThread是一個啓動好Looper的Thread對象,方便將Handler綁定在該線程中。ide

HandlerThread簡單使用

//建立子線程Handler
HandlerThread mHandlerThread = new HandlerThread("daqi");
mHandlerThread.start();
Handler mHandler = new Handler(mHandlerThread.getLooper());
mHandler.post(new Runnable() {
    @Override
    public void run() {

    }
});
複製代碼

HandlerThread源碼分析

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

IntentService源碼分析

        爲何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,結束工做線程。

相關文章
相關標籤/搜索