除了Thread外,在Android中扮演線程的角色還有很多,比如AsyncTask和IntentService,同時HandlerThread也是一種特殊的線程。
盡管AsyncTask和IntentService以及HandlerThread的表現(xiàn)形式有區(qū)別于傳統(tǒng)的線程,但是本質(zhì)上仍然是傳統(tǒng)的線程。
AsyncTask
AsyncTask是一種輕量級的異步任務(wù)類,它可以在線程池中執(zhí)行后臺任務(wù),并把執(zhí)行進(jìn)度和最終結(jié)果傳遞到主線程并在主線程中更新UI。
AsyncTask封裝了Thread和Handler。
AsyncTask并不適合特別耗時的后臺任務(wù),對于特別耗時的后臺任務(wù),建議交給線程池。
- AsyncTask的類聲明
/**
*@Params 參數(shù)類型
*@Progress 后臺任務(wù)的執(zhí)行進(jìn)度類型
*@Result 后臺任務(wù)的返回結(jié)果的類型
public abstract class AsyncTask<Params,Progress,Result>
AsyncTask提供了4個核心方法,他們的含義如下:
- onPreExecute() 在主線程中執(zhí)行,在異步任務(wù)執(zhí)行前被調(diào)用,一般可以用于做一些準(zhǔn)備工作。
- doInBackground(Params...params) 在線程池中執(zhí)行,此方法用于執(zhí)行異步任務(wù) ,params參數(shù)表示異步任務(wù)輸入的參數(shù)。在此方法中可以通過publicProgress方法來更新任務(wù)的進(jìn)度,publicProgress方法會調(diào)用onPublicUpdate方法。另外此方法需要返回計算機(jī)過給onPostExecute方法。
- onProgressUpdate(Progress...values)在主線程中執(zhí)行,當(dāng)后臺任務(wù)的執(zhí)行進(jìn)度放生改變時被調(diào)用。
- onPostExecute(Result...result)在主線程中執(zhí)行,在異步任務(wù)執(zhí)行之后,此方法會被調(diào)用,其中result是后臺任務(wù)被取消時調(diào)用。這個時候onPostExecute()不會被調(diào)用
除了上述的四個方法外,AsyncTask還提供onCancelled()方法,它同樣在主線程中執(zhí)行,當(dāng)異步任務(wù)被取消時調(diào)用。這個時候onPostExecute()不會被調(diào)用。
- 示例
private class DownloadFilesTask extends AsyncTask<URL,Interger,Long>{
protected Long doInBackground(URL...urls){
int count = urls.length;
long totalSize = 0;
for (int i=0;i<count;i++){
totalSize += DownLoader.downloadFile(urls[i]);
publishProgress((int)(i/(float)count*100));
// Escape early if cancel() is called
if (isCancelled()){
break;
}
}
return totalSize;
}
protected void onProgressUpdate(Interger... progress){
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result){
showDialog("Download"+result+"bytes");
}
}
- 使用限制
- AsyncTask的類必須在主線程中加載。
- AsyncTask的對象必須在主線程中加載。
- execute必須在UI線程中調(diào)用。
- 不要在程序中直接調(diào)用onPreExecute(),onPostExecute,doInBackground()和onProgressUpdate方法。
- 一個AsyncTask對象只能執(zhí)行一次,即只能調(diào)用一次execute方法,否則回報異常。
- 從Android3.0開始,為避免AsyncTask所帶來的并發(fā)錯誤,AsyncTask又采用一個線程來執(zhí)行任務(wù)。盡管如此,Android3.0的后續(xù)版本,我們?nèi)钥梢酝ㄟ^AsyncTask的executeOnExecutor方法來并行執(zhí)行任務(wù)。
AsyncTask的工作原理
- execute方法
public final AsyncTask<Params,Progress,Result> execute(Params... params){
return executeExecutor(sDefaultExecutor,params);
}
public final AsyncTask<Params,Progress,Result> executeOnExecutor(Executor exec, Params... params){
if(mStatus!=Status.PENDING) {
switch(mStatus){
case RUNNING:
throw new IllegalStateException("Cannot execute task:"+"the task is already running");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"+"the task has already been executed"+"(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
sDefaultExecutor實際是一個串行的線程池,一個進(jìn)程中所有的AsyncTask全部在這個串行的線程池中執(zhí)行。
- 線程池執(zhí)行
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor{
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchroized void execute (final Runnable r){
mTasks.offer(new Runnable(){
public void run(){
try{
r.run();
}finally{
scheduleNext();
}
}
});
if(mActive==null){
scheduleNext();
}
}
protected synchroized void scheduleNext(){
if((mActive==mTasks.poll())!=null){
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
首先系統(tǒng)會把AsyncTask的Params參數(shù)封裝FutureTask對象,F(xiàn)utureTask是一個并發(fā)類,在這個它充當(dāng)Runnable的作用。
接著FutureTask會交給SerialExecutor的execute去處理,SerialExecutor的execute方法首先會把FutureTask對象插入到任務(wù)隊列mTasks中,如果這時候沒有正在活動的AsyncTask任務(wù),那么就會調(diào)用SerialExcutor的scheduleNext方法來執(zhí)行下一個AsyncTask任務(wù)。
同時當(dāng)一個AsyncTask執(zhí)行完之后,AsyncTask會繼續(xù)執(zhí)行其他任務(wù)直到所有任務(wù)都被執(zhí)行為止,從這一點(diǎn)可以看出,默認(rèn)情況下AsyncTask是串行執(zhí)行。
AsyncTask有兩個線程池(SerialExcutor和THREAD_POOL_EXECUTOR)和一個Handler(IntentHandler)
SerialExecutor用于任務(wù)排隊,THREAD_POOL_EXECUTOR
用于真正的執(zhí)行任務(wù)。
IntentHandler用于將執(zhí)行環(huán)境從線程池切換到主線程。
AsyncTask的構(gòu)造方法中如下一段代碼,由于FutureTask的run方法會調(diào)用mWorker的call方法,因此mWorker的call方法最終會在線程池中執(zhí)行。
mWorker = new WorkerRunnable<Params,Result>(){
public Result call() throws Exception{
mTaskInvoked.set(true);
Process.setThreadPriority(Progress.THREAD_PRIORITY_BACKGROUND);
// noinspection unchecked
return postResult(doInBackground(mParams));
}
};
在mWorker的call方法中,首先將mTaskInvoked設(shè)為true,表示當(dāng)前任務(wù)已經(jīng)被調(diào)用過了,然后執(zhí)行AsyncTask的doInBackground方法,接著將其返回值傳遞給postResult方法。
private Result postResult(Result result){
@SuppressWarnings("unchecked)
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this,result));
message.sendToTarget();
return result;
}
postResult方法會通過sHandler發(fā)送一個MESSAGE_POST_RESULT的消息,這個sHandler定義如下:
private static final InternalHandler sHandler = new InternalHandler();
private static class InteralHandler extends Handler{
@SuppressWarnings({"unchecked","RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg){
AsyncTaskResult result = (AsyncTaskResult)msg.obj;
switch(msg.what){
case MESSAGE_POST_RESULT:
//Thread is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
sHandler會一個靜態(tài)的Handler對象,為了能夠?qū)?zhí)行環(huán)境切換到主線程,這就要求sHandler這個對象必須在主線程中創(chuàng)建。由于靜態(tài)成員會在加載類的時候進(jìn)行初始化,因此變相要求AsyncTask的類必須在主線程中加載,否則同一線程中的AsyncTask都無法工作。sHandler收到MESSAGE_POST_RESULT這個消息后回調(diào)AsyncTask的finish方法。
private void finish(Result result){
if(isCancelled){
onCancelled(result);
}else{
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果AsyncTask被取消了被調(diào)用onCancelled方法,否則就調(diào)用onPostExecute方法,可以看出doInBackground中的result會傳遞給onPostExecute方法。
如果要并行執(zhí)行,要使用executeOnExcutor方法。
HandlerThread
HandlerThread繼承了Thread,是一種可以使用Handler的Thread,它的實現(xiàn)也很簡單,就是在run方法中通過Looper.prepare
創(chuàng)建消息隊列,并通過Looper.loop來開啟消息循環(huán),這樣在實際中就允許在HandlerThread中創(chuàng)建Handler了,HandlerThread的run方法如下所示:
public void run(){
mTid = Progress.myTid();
Looper.prepare();
synchroized(this){
mLooper = Looper.myLooper();
notifyAll();
}
Progress.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
HnadlerThread在內(nèi)部創(chuàng)建了一個消息隊列,外部需要通過Handler的消息方式來通知HandlerThread執(zhí)行一個具體任務(wù)。
HandlerThread的run是一個無限循環(huán),因此當(dāng)明確不需要時通過它的quit或者quitSafely方法終止線程運(yùn)行。
一個具體的應(yīng)用場景就是IntentService。
IntentService
IntentService是一種特殊的Service,它繼承Service并且它是一個抽象類,因此創(chuàng)建它的子類才能使用IntentService。
IntentService可用于執(zhí)行后臺耗時任務(wù),當(dāng)任務(wù)執(zhí)行后它會自動停止,同時由于IntentService是服務(wù)的原因,導(dǎo)致它的優(yōu)先級比單純的線程要高很多,所以IntentService適合執(zhí)行一些高優(yōu)先級的后臺任務(wù)。
IntentService封裝了HandlerThread和Handler。
- onCreate
public void onCreate(){
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService["+mName+"]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceHandler);
}
當(dāng)IntentService被第一次啟動時,它的onCreate方法會被調(diào)用,onCreate方法會創(chuàng)建一個HandlerThread,然后使用它的Looper來構(gòu)造一個Handler對象mServiceHandler,這樣通過mServiceHandler發(fā)送的消息都會在HandlerThread中執(zhí)行,從這個角度看, IntentService也可以用于執(zhí)行后臺任務(wù)。每次啟動IntentService,它的onStartCommand方法就會調(diào)用一次。IntentService在onStartCommand中處理每個任務(wù)的Intent。onStartCommand方法中調(diào)用start方法處理Intent。
- onStartCommand的start方法
public void onStart(Intent intent, int startId){
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
IntentService僅僅是用過mServiceHandler發(fā)送一個消息,這個消息會在HandlerThread中處理。
mServiceHandler收到消息后,會將Intent對象傳遞給onHandlerIntent方法去處理。注意這個Intent對象的內(nèi)容和外界startService(Intent)中的intent的內(nèi)容完全一致的,通過這個Intent對象即可解析出外界啟動IntentService時所傳遞的參數(shù),通過這些參數(shù)就可區(qū)分具體的后臺任務(wù),這個在onHandlerIntent方法中就可以對不同的后臺任務(wù)做處理了。當(dāng)onHandlerIntent方法執(zhí)行結(jié)束后,IntentService會通過stopSelf()來停止服務(wù),那是因為stopSelf()會立刻停止服務(wù),而這個時候可能還有其他消息未處理,stopSelf(int startId)則會等待所有的消息都處理完畢后才終止服務(wù)。一般來說stopSelf(int startId)在嘗試停止服務(wù)之前會判讀最近啟動服務(wù)的次數(shù)是否和startId相等,如果相等就立即停止服務(wù),不相等則不停止服務(wù)。
- ServiceHandler的實現(xiàn)
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);
}
}
IntentService的onHandleIntent是一個抽象方法,它需要我們在子類中實現(xiàn),它的作用是從Intent參數(shù)中區(qū)分具體的任務(wù)并執(zhí)行這些任務(wù)。如果目前只存在一個后臺任務(wù),那么onHandleIntent方法執(zhí)行完這個任務(wù)后,stopSelf(int startId)就會直接停止服務(wù);如果目前存在多個后臺任務(wù),那么當(dāng)onHandleIntent方法執(zhí)行完最后一個任務(wù)時,stopSelf(int startId)才會直接停止服務(wù)。另外,由于每執(zhí)行一個后臺任務(wù)就必須啟動一次IntentService,而IntentService內(nèi)部則通過消息的方式向HandlerThread請求執(zhí)行任務(wù),Handler中Looper是順序處理消息的,這意味著IntentService也會順序執(zhí)行后臺任務(wù),當(dāng)有多個后臺任務(wù)同時存在時,這些后臺任務(wù)會按照外界發(fā)起的順序排隊執(zhí)行。
Android中的線程池
線程池的優(yōu)點(diǎn)
- 重用線程池中的線程,避免因為線程的創(chuàng)建和銷毀所帶來的性能開銷。
- 能有效的控制線程池的最大并發(fā)量,避免大量的線程因為搶占系統(tǒng)資源而造成的堵塞的現(xiàn)象。
- 能夠?qū)€程進(jìn)行簡單的管理,提供定時執(zhí)行以及執(zhí)行間隔循環(huán)等功能。
ThreadPoolExecutor
- 構(gòu)造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
corePoolSize 線程池的核心線程數(shù),默認(rèn)情況下,核心線程會在線程池中一直存活,即使它們處于閑置狀態(tài)。如果將ThreadPoolExecutor的allowCorePoolTimeOut屬性設(shè)為true,那么閑置的核心線程在等待執(zhí)行任務(wù)時會有超時策略,超時時間由keepAliveTime所指定。當(dāng)?shù)却龝r間超過keepAliveTime所指定的時長后,核心線程就會停止。
maximumPoolSize 線程池所能容納的最大線程數(shù),當(dāng)活動線程數(shù)達(dá)到這個數(shù)值后,后續(xù)的新任務(wù)就會被堵塞。
keepAliveTime 非核心線程閑置時的超時時長,超過這個時長,非核心線程就會被回收。當(dāng)ThreadPoolExecutor的
allowCorePoolTimeOut屬性為true時,keepAliveTime同樣會作用于核心線程。
unit 用于指定keepAliveTime的時間單位。
workQueue 線程池中的任務(wù)隊列,通過execute提交的runnable對象會存儲在這個參數(shù)中。
threadFactory 線程工廠,為線程池提供創(chuàng)建新線程的功能。ThreadFactory是一個接口,它只有一個方法:Thread newThread
(Runnable r)。
除了上面的這些主要參數(shù)外,ThreadPoolExecutor還有一個不常用的參數(shù)RejectedExecutionHandler handler。當(dāng)線程無法執(zhí)行新任務(wù)時,這可能是由于任務(wù)隊列已滿或者是無法成功執(zhí)行任務(wù),這個時候ThreadPoolExecutor會調(diào)用handler的rejectedExecution方法來通知調(diào)用者,默認(rèn)情況下rejectedExecution方法會拋出一個RejectedExecutionexception。
ThreadPoolExecutor為RejectExecutionHandler提供了幾個可選值:
CallerRunsPolicy、AbortPolicy、DiscardOldestPolicy,其中AbortPolicy是默認(rèn)值,它會直接拋出RejectedExecutionException,handler這個參數(shù)不常用。
- ThreadPoolExecutor執(zhí)行任務(wù)時大致遵循以下原則:
- 如果線程池的數(shù)量未達(dá)到核心線程數(shù)量,那么就會啟動一個核心線程來執(zhí)行任務(wù)。
- 如果線程池的線程數(shù)量已達(dá)到或者超過核心線程數(shù)量,那么任務(wù)會被插入到任務(wù)隊列中排隊執(zhí)行。
- 如果步驟2中無法將任務(wù)插入到任務(wù)隊列中,這往往是由于任務(wù)隊列已滿,這個時候如果線程數(shù)量未達(dá)到線程池規(guī)定的最大值,那么就會立即啟動一個非核心線程來執(zhí)行任務(wù)。
- 如果步驟3中線程數(shù)量已達(dá)到線程池所規(guī)定的最大值,那么就拒絕執(zhí)行這次任務(wù),ThreadPoolExecutor會調(diào)用RejectedExcutionHandler的rejectedExecution來通知調(diào)用者。
- ThreadPoolExecutor在AsyncTask中的體現(xiàn)。
private static final int CPU_COUNT = Runtime.getRuntime().AcailableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT+1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT*2+1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory(){
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r){
return new Thread(r,"AsyncTask #"+mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory)
線程池的分類
FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONEDS,new LinkedBlockingQueue<Runnable>());
}
通過Executors的newFixedThreadPool方法來創(chuàng)建,它是一種線程數(shù)量固定的線程池,當(dāng)線程處于空閑狀態(tài)時,它們并不會被回收,除非線程池被關(guān)閉了。當(dāng)所有線程都處于活動狀態(tài)時,新任務(wù)都會處于等待狀態(tài),直到線程空閑出來。由于FixedThreadPool只有核心線程并且核心線程不會被回收,這意味著他能夠更加快速的響應(yīng)外界的請求。FixedThreadPool中只有核心線程并且核心線程沒有超時機(jī)制,另外任務(wù)隊列也是沒有大小限制。
CachedThreadPool
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONEDS,new SynchronousQueue<Runnable>())
}
通過Executors的呢我CachedThreadPool方法來創(chuàng)建。它是一種線程數(shù)量不定的線程池,它只是非核心線程,并且其最大線程數(shù)為Integer.MAX_VALUE。由于Integer_MAX_VALUE是一個很大的數(shù),實際上就相當(dāng)于最大線程數(shù)可以死任意大。當(dāng)線程池中的線程都處于活動狀態(tài)時,線程池會創(chuàng)建新的線程來處理新任務(wù),否則就會利用空閑的線程來處理新任務(wù)。線程池中的空閑線程都有超時機(jī)制,CachedThreadPool的任務(wù)隊列其實相當(dāng)于一個空集合,這將導(dǎo)致任何任務(wù)都會立即執(zhí)行,因為在這種場景下SynchronousQueue是無法插入任務(wù)的。SynchronousQueue是一個非常特殊的隊列,在很多情況下可以把它簡單理解為一個無法存儲元素的隊列,由于它實際中較少使用,這里就不深入探討它了。從CachedThreadPool的特性來看,這類線程池比較適合執(zhí)行大量的耗時較少的任務(wù)。當(dāng)中整個線程池都處于閑置狀態(tài)時,線程池中的線程都或超時而被停止,這個時候CachedThreadPool之中實際上是沒有任何線程的,它幾乎不占任何系統(tǒng)資源的。
ScheduledThreadPool
public static ScheduleExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize,Integer.MAX_VALUE,0,NAMOSECONEDS,new DelayWorkQueue());
}
通過Executors的newScheduledThreadPool方法來創(chuàng)建。它的核心線程數(shù)量是固定的,而非核心線程是沒有限制的,并且當(dāng)非核心線程閑置時會被立即回收。ScheduledThreadPool這列線程池主要用于執(zhí)行定時任務(wù)和具有固定周期的重復(fù)任務(wù)。
SingleThreadPool
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegateExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
通過Executors的newSingleThreadExecutor方法來創(chuàng)建。這類線程池內(nèi)部只有一個核心線程,它確保所有任務(wù)都在同一個線程中順序執(zhí)行。SingleThreadExecutor的意義在于統(tǒng)一所有外界任務(wù)到一個線程中,這使得在這些任務(wù)之間不需要出來線程同步的問題。
- 使用方法
Runnable command = new Runnable(){
@Override
public void run(){
SystemClock.sleep(2000);
}
};
ExecutorService fixedThreadPool = Executors.newDixedThreadPool(4);
fixedThreadPool.execute(command);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(command);
ScheduleExecutorsService scheduledThreadPool = Executors.newScheduledThreadPool(4);
//2000ms后執(zhí)行command
scheduledThreadPool.schedule(command,2000,TimeUnit.MILLISECONDS);
//延遲10ms后,每隔1000ms執(zhí)行一次command
scheduledThreadPool.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(command);