HandlerThread
HandlerThread繼承了Thread,它是一種可以使用Handler的Thread,它的實現也很簡單,就是在run方法中通過Looper.prepare()來創建消息隊列,並通過Looper.loop()來開啓消息循環,這樣在實際的使用中就允許在HandlerThread中創建Handler了。HandlerThread的run方法如下所示。
@Override
public void run() {
mTid = Process.myTid();
// 創建當前線程的Looper對象
Looper.prepare();
synchronized (this) {
// 獲取當前Looper對象
mLooper = Looper.myLooper();
// 喚醒wait等待
notifyAll();
}
// 設置當前線程的優先級
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 循環取消息,沒有消息則等待
Looper.loop();
mTid = -1;
}
從HandlerThread的實現來看,它和普通的Thread有顯著的不同之處。普通Thread主要用於在run方法中執行一個耗時任務,而HandlerThread在內部創建了消息隊列,外界需要通過Handler的消息方式來通知HandlerThread執行一個具體的任務。HandlerThread是一個很有用的類,它在Android中的一個具體的使用場景是IntentService。由於HandlerThread的run方法是一個無限循環,因此當明確不需要再使用HandlerThread時,可以通過它的quit或者quitSafely方法來終止線程的執行,這是一個良好的編程習慣。
HandlerThread 獲取Looper方法
public Looper getLooper() {
// 判斷是否啓動了線程
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
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;
}
IntentService工作原理
IntentService是一種特殊的Service,它繼承了Service並且它是一個抽象類,因此必須創建它的子類才能使用IntentService。IntentService可用於執行後臺耗時的任務,當任務執行後它會自動停止,同時由於IntentService是服務的原因,這導致它的優先級比單純的線程要高很多,所以IntentService比較適合執行一些高優先級的後臺任務,因爲它優先級高不容易被殺死。在實現上,IntentService封裝了HandlerThread和Handler,這一點可以從它的onCreate方法中看出來,如下所示。
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
// 創建HandlerThread線程對象,並設置線程名字
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
// 開啓線程,執行HandlerThread的run方法
thread.start();
- // 獲取HandlerThread線程中的Looper對象
mServiceLooper = thread.getLooper();
// 創建Handler,並設置爲HandlerThread的Looper對象
mServiceHandler = new ServiceHandler(mServiceLooper);
}
當IntentService被第一次啓動時,它的onCreate方法會被調用,onCreate方法會創建一個HandlerThread,然後使用它的Looper來構造一個Handler對象mServiceHandler,這樣通過mServiceHandler發送的消息最終都會在HandlerThread中執行,從這個角度來看,IntentService也可以用於執行後臺任務。每次啓動IntentService,它的onStartCommand方法就會調用一次,IntentService在onStartCommand中處理每個後臺任務的Intent。下面看一下onStartCommand方法是如何處理外界的Intent的,onStartCommand調用了onStart,onStart方法的實現如下所示。
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
可以看出,IntentService僅僅是通過mServiceHandler發送了一個消息,這個消息會在HandlerThread中被處理。mServiceHandler收到消息後,會將Intent對象傳遞給onHandleIntent方法去處理。注意這個Intent對象的內容和外界的startService(intent)中的intent的內容是完全一致的,通過這個Intent對象即可解析出外界啓動IntentService時所傳遞的參數,通過這些參數就可以區分具體的後臺任務,這樣在onHandleIntent方法中就可以對不同的後臺任務做處理了。當onHandleIntent方法執行結束後,IntentService會通過stopSelf(int
startId)方法來嘗試停止服務。這裏之所以採用stopSelf(int startId)而不是stopSelf()來停止服務,那是因爲stopSelf()會立刻停止服務,而這個時候可能還有其他消息未處理,stopSelf(int startId)則會等待所有的消息都處理完畢後才終止服務。一般來說,stopSelf(int startId)在嘗試停止服務之前會判斷最近啓動服務的次數是否和startId相等,如果相等就立刻停止服務,不相等則不停止服務。
ServiceHandler的實現如下所示。
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 讓子類去處理業務
onHandleIntent((Intent)msg.obj);
// 停止服務
stopSelf(msg.arg1);
}
}
protected abstract void onHandleIntent(Intent intent);
public void onDestroy() {
// 退出Looper循環
mServiceLooper.quit();
}
IntentService的onHandleIntent方法是一個抽象方法,它需要我們在子類中實現,它的作用是從Intent參數中區分具體的任務並執行這些任務。如果目前只存在一個後臺任務,那麼onHandleIntent方法執行完成這個任務後,stopSelf(int startId)就會直接停止服務;如果目前存在多個後臺任務,那麼當onHandleIntent方法執行完最後一個任務是,stopSelf(int
startId)纔會直接停止服務。另外,由於每執行一個後臺任務就必須啓動一次IntentService,而IntentService內部則通過消息的方式向HandlerThread請求執行任務,Handler中的Looper是順序處理消息的,這就意味着IntentService也是順序執行後臺任務的,當有多個後臺任務同時存在時,這些後臺任務會按照外界發起的順序排隊執行。