在Android開發(fā)中,我們知道可以在主線程中直接使用Handler,這是因為在APP的入口,系統(tǒng)就已經(jīng)調(diào)用Looper.prepareMainLooper(),和Looper.loop(),但是如果我們想在子線程使用handler颗味,也很簡單浦旱,只要先使用Looper.prepare();然后調(diào)用Looper.loop()后痢掠,就可使用handler了产还,但是可能有些小伙伴覺得也麻煩厘熟,是不是應(yīng)該有一種對這種進行封裝的框架呢屯蹦?沒錯,系統(tǒng)里有個HandlerThread就是干這件事的绳姨。
我們來看看HandlerThread的使用:
HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
Looper looper = thread.getLooper();
MyHandler myHandler = new MyHandler(this,looper);
首先登澜,創(chuàng)建一個HandlerThread對象,然后必須先調(diào)用start()方法飘庄,因為HandlerThread是繼承Thread的脑蠕,調(diào)用start()開啟線程的執(zhí)行,可以看一下其run()方法的源碼:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
可以看到跪削,run方法里面做了對Looper的初始化操作谴仙,初始化完成后回調(diào)了onLooperPrepared()方法,當(dāng)我們需要做一些在Looper初始化完成并在開啟輪詢之前的操作的時候碾盐,我們就可以繼承該類然后復(fù)寫自己的onLooperPrepared方法做我們需要的工作狞甚,這樣設(shè)計增加了其擴展性。
下面我們回到主線程的使用當(dāng)中廓旬,接著調(diào)用thread.getLooper()哼审,看一下該方法源碼:
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;
}
首先,判斷Looper的線程是否存活的孕豹,然后進入同步代碼塊中涩盾,此代碼塊是個while循環(huán),如果線程是存活的并且mLoope這時還沒有被初始化好励背,這時該線程(主線程)就會等待春霍,等到mLoope初始化好之后,子線程會調(diào)用notifyAll()來喚醒叶眉,然后返回mLooper址儒。
接下來我們就可以使用返回的Looper創(chuàng)建Handler了:
MyHandler myHandler = new MyHandler(this,looper);
這樣通過此handler發(fā)送的消息都會被創(chuàng)建此Looper的子線程拿去處理了芹枷。
我們再來看一下HandlerThread的一個典型的應(yīng)用:系統(tǒng)為我們提供的IntentService ,先貼出其源碼:
package android.app;
import android.annotation.WorkerThread;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
/**
* IntentService is a base class for {@link Service}s that handle asynchronous
* requests (expressed as {@link Intent}s) on demand. Clients send requests
* through {@link android.content.Context#startService(Intent)} calls; the
* service is started as needed, handles each Intent in turn using a worker
* thread, and stops itself when it runs out of work.
*/
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
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);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
/**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* This method is invoked on the worker thread with a request to process.
* Only one Intent is processed at a time, but the processing happens on a
* worker thread that runs independently from other application logic.
* So, if this code takes a long time, it will hold up other requests to
* the same IntentService, but it will not hold up anything else.
* When all requests have been handled, the IntentService stops itself,
* so you should not call {@link #stopSelf}.
*
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
* This may be null if the service is being restarted after
* its process has gone away; see
* {@link android.app.Service#onStartCommand}
* for details.
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
先介紹一下IntentService莲趣,這是一個Service的子類鸳慈,專門用來在子線程處理異步任務(wù)的,處理完成之后會自動的結(jié)束服務(wù)喧伞。
我們可以看到走芋,在onCreate()方法中使用了HandlerThread,和我們之前那段代碼幾乎差不多潘鲫,在onStart方法中通過Handler將intent信息發(fā)送到消息隊列中翁逞,然后在子線程中調(diào)用handleMessage方法去處理 ,這里可以看到handleMessage方法中溉仑,先調(diào)用了onHandleIntent((Intent)msg.obj)挖函,這是個抽象方法,需要我們自己去實現(xiàn)浊竟,我們可以在其中做一些耗時的任務(wù)挪圾,任務(wù)完成之后會自動調(diào)用stopSelf(msg.arg1)結(jié)束服務(wù)。在onDestroy中調(diào)用mServiceLooper.quit()結(jié)束消息隊列逐沙,防止此Service出現(xiàn)內(nèi)存泄漏哲思。當(dāng)我們多次調(diào)用startService開啟服務(wù)時,會走onStartCommand方法吩案,從源碼中看到棚赔,它也會調(diào)用onStart(intent, startId)方法,然后將intent信息發(fā)送過去徘郭,由此靠益,我們能得出,當(dāng)有多個任務(wù)時残揉,這些任務(wù)都被依次的存入子線程的消息隊列中胧后,待子線程一個一個取出去處理,如果前面的任務(wù)耗時抱环,則會阻塞后面的任務(wù)壳快。
這里有一點需要了解一下,前面講了在任務(wù)處理完成之后會調(diào)用stopSelf(msg.arg1)結(jié)束服務(wù)镇草,那么你也許會問:如果消息隊列中有多個任務(wù)眶痰,既然第一個任務(wù)完成之后就把服務(wù)給結(jié)束了,那之后的任務(wù)怎么辦梯啤?
這里我們就需要來看看stopSelf(msg.arg1)的源碼了:
/**
* Old version of {@link #stopSelfResult} that doesn't return a result.
*
* @see #stopSelfResult
*/
public final void stopSelf(int startId) {
if (mActivityManager == null) {
return;
}
try {
mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}
可以看到是通過AIDL調(diào)用ActivityManagerService來停止服務(wù)的竖伯,關(guān)于怎么關(guān)閉的,這里就不介紹了,有興趣的可以去看看源碼七婴,這里我們看其官方注釋祟偷,提示此方法是stopSelfResult的老版本,我們來看看stopSelfResult的源碼:
/**
* Stop the service if the most recent time it was started was
* <var>startId</var>. This is the same as calling {@link
* android.content.Context#stopService} for this particular service but allows you to
* safely avoid stopping if there is a start request from a client that you
* haven't yet seen in {@link #onStart}.
*
* <p><em>Be careful about ordering of your calls to this function.</em>.
* If you call this function with the most-recently received ID before
* you have called it for previously received IDs, the service will be
* immediately stopped anyway. If you may end up processing IDs out
* of order (such as by dispatching them on separate threads), then you
* are responsible for stopping them in the same order you received them.</p>
*
* @param startId The most recent start identifier received in {@link
* #onStart}.
* @return Returns true if the startId matches the last start request
* and the service will be stopped, else false.
*
* @see #stopSelf()
*/
public final boolean stopSelfResult(int startId) {
if (mActivityManager == null) {
return false;
}
try {
return mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
return false;
}
代碼和stopSelf幾乎差不多打厘,就是返回值從void改變?yōu)閎oolean修肠,意味著這個并方法不一定能真正的停掉服務(wù),我們再來看看方法參數(shù)和返回值的注釋:
@param startId The most recent start identifier received in {@link #onStart}.
@return Returns true if the startId matches the last start request and the service will be stopped, else false.
startId是通過onStart方法接收的ID婚惫。
如果需要關(guān)閉的startId和最后一次請求服務(wù)的時候的startId相匹配,就停止服務(wù)魂爪,如果不匹配先舷,則不停止。什么意思滓侍?就是說如果你多次調(diào)用startService啟動了服務(wù)蒋川,此時最后“最后一次請求服務(wù)的時候的startId”就是你最后請求的那個startID,也就是說只有最后的那個startID才能停止掉服務(wù)撩笆,之前的都不行捺球,明白了吧。其實夕冲,這才符合實際氮兵,如果你的Service要同時處理多個請求,你就不能在當(dāng)前一個請求處理完成之后立刻停止Service歹鱼,因為很可能現(xiàn)在你已經(jīng)收到了一個新的啟動Service請求(如果立刻停止泣栈,那么新來的請求就會跟著終止)。為了避免這種情況發(fā)生弥姻,你可以用stopSelf(int)來保證你當(dāng)前停止Service的請求是基于上一個請求的南片。也就是說,當(dāng)你調(diào)用stopSelf(int)庭敦,你把startID傳給了對應(yīng)的要停止的Service疼进,這個startID是上一個請求的StartID!!如果沒有第二個請求來秧廉,那么這個Service就會死掉伞广,但是如果這個Service已經(jīng)又接受到一個新的啟動請求之后,你才調(diào)用stopSelf(int)疼电,那么你傳遞給stopSelf()的ID是上一個請求的ID赔癌,而當(dāng)前Service的startID已經(jīng)更新為新的請求的ID,造成兩個ID不對應(yīng)澜沟,stopSelf()失效灾票,那么Service就不會停止。這樣就避免了將后面的請求終止茫虽。
最后總結(jié)一下:
- 對于HandlerThread的使用刊苍,要先調(diào)用其start方法做Looper的初始化操作既们,然后調(diào)用HandlerThread的getLooper方法來獲取looper,此方法是個阻塞的方法正什,直到Looper初始化完成才能被喚醒啥纸,最后將looper傳入Handle中使用。
- IntentService是系統(tǒng)為我們封裝的一個Service婴氮,其原理是通過HandlerThread來實現(xiàn)的斯棒,我們在使用時只需基礎(chǔ)IntentService并實現(xiàn)onHandleIntent方法,在方法中具體處理我們的耗時任務(wù)主经,如果有多個任務(wù)荣暮,則這些任務(wù)會在子線程的消息隊列中排隊等待被處理,并且處理完最后一個任務(wù)時罩驻,才會停止掉該服務(wù)穗酥。